java从入门到精通API01

文章目录

1 API1

API:Application Programming Interface应用编程接口,一切可以调用的东西都是API。

java.lang包,这个包会自动导入。
java.lang.Object
java.lang.String
java.lang.StringBuilder/StringBuffer

正则表达式
包装类等

1.1 Object

1.1.1 概念

所有对象的顶级父类
存在于java.lang包中,这个包不需要我们手动导包
在这里插入图片描述

1.1.2 常用方法

boolean equals(Object obj) 
          指示其他某个对象是否与此对象“相等”。 
protected  void finalize() 
          当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。 
 int hashCode() 
          返回该对象的哈希码值。 
 String toString() 
          返回该对象的字符串表示。

1.1.3 toString()

默认返回 类名@地址 的格式,来展示对象的地址值,如:a00000.Student@a0834e7。
如果想看属性值我们可以重写这个方法,重写后返回的就是把属性值拼接成一个字符串。
如:Student [name=苏大强, age=20, id=10001]

package cn.tedu.object;
//测试Object用法
public class Test1_Object {
    
    
	public static void main(String[] args) {
    
    
		//创建对象测试
		Person p = new Person("熊大",20,10,"森林");
		System.out.println(p);//要是不重写toString()就是使用的Object提供的默认方式只能打印对象在内存的地址
		System.out.println(p);//由于重写了toString()所以打印的是属性的值
	}
}
//定义类,提供属性
class  Person{
    
    
	//构造:source-generate constrctor using fileds
	public Person() {
    
    }
	public Person(String name, int age, double salary, String addr) {
    
    
		this.name = name;
		this.age = age;
		this.salary = salary;
		this.addr = addr;
	}
	//属性
	private String name;
	private int age;
	private double salary;
	private String addr;
	//set() get()
	public String getName() {
    
    
		return name;
	}
	public void setName(String name) {
    
    
		this.name = name;
	}
	public int getAge() {
    
    
		return age;
	}
	public void setAge(int age) {
    
    
		this.age = age;
	}
	public double getSalary() {
    
    
		return salary;
	}
	public void setSalary(double salary) {
    
    
		this.salary = salary;
	}
	public String getAddr() {
    
    
		return addr;
	}
	public void setAddr(String addr) {
    
    
		this.addr = addr;
	}
	
	//toString():source - generate toString()-ok
	@Override//重写的是Object提供的toString()是为了查看属性值
	public String toString() {
    
    
		return "Person [name=" + name + ", age=" + age + ", salary=" + salary + ", addr=" + addr + "]";
	}
	
}

1.1.4 equals(Object obj)

当前对象和参数对象比较大小,默认是比较内存地址,如果要比较对象的属性,可以重写该方法。

package javase.base;

import javase.base.extend.Tiger;

public class TestArea {
    
    
	public static void main(String[] args) {
    
    
		//基础类型是按值比较
		int i1 = 10;
		int i2 = 10;
		int i3 = 12;
		System.out.println(i1==i2);//true
		System.out.println(i1==i3); //false
		
		//对象为引用类型,引用类型按内存地址比较
		Tiger t1 = new Tiger();
		Tiger t2 = new Tiger();
		Tiger t3 = t1;
		
		System.out.println(t1);
		System.out.println(t2);
		System.out.println(t3);
		
		System.out.println(t1.equals(t2));//false
		System.out.println(t1 == t2);//false

		System.out.println(t1.equals(t3));//true
		System.out.println(t1 == t3);//true
	}
}

1.1.5 hashCode()

返回该对象的哈希码值。

package cn.tedu.object;
//测试equals()  ==
public class Test2_Object2 {
    
    
	public static void main(String[] args) {
    
    
		//1,==比较八大基本类型时:判断的是值
		int a = 1;
		int b = 1;
		System.out.println(a==b);//true
		
		
		//2,==比较对象时,比较的是对象的地址值
		Integer x = 5;
		Integer y = 10;
		Integer z = x;//把x的地址赋值给了z
		
		System.out.println(x==y);//false
		System.out.println(x==z);//true
		
		//3,equals()用来比较对象存着的值
		System.out.println(x.equals(z));//true
		
		//4,测试hashCode()哈希值
		System.out.println(x.hashCode());//5
		System.out.println(y.hashCode());//10
		System.out.println(z.hashCode());//5
		
	}
}

1.2 String

字符串对象

1.2.1 特点

是一个封装char[]数组的对象
在这里插入图片描述

字符串不可变
在这里插入图片描述

1.2.2 创建String对象

在这里插入图片描述

1、 如果是第一次使用字符串,java会在字符串常量池创建一个对象。
2、 再次使用相同的内容时,会直接访问常量池中存在的对象。
方式1:new String(char[])

其实字符串底层维护了一个char[]
char[] c = {
    
    'a','b','c','d'};
String s = new String(c);//堆中分配新的内存
System.out.println(s);

方式2:直接创建
常量池里直接创建对象(本质还是char[]),再次使用相同内容,会去常量池中找到已经存在的对象,不会新建。

String s2="abcd";//常量池中分配新的内存
System.out.println(s2);
System.out.println(s==s2);//地址不同
System.out.println(s.equals(s2));//内容相同
				
//如果使用过了就不再创建,引用存在的对象
String s3="abcd";//访问常量池中已经存在的对象
System.out.println(s3==s2);//true

1.2.3 字符串连接效率

利用String类,在做字符串拼接的过程效率极其低下。

String s1="aaa";
String s2="bbb";
String s3="ccc";
String s4=s1+s2+s3;//字符串不可变,每次加会创建新对象,这行代码要产生右侧的5个新对象,慢

package a00000;
public class TT {
    
    
	public static void main(String[] args) {
    
    
		String s = "abcdefghijklmnopqrstuvwxyz";
		String news="";//字符串是不能被修改的,拼接时,每次会创建对象,
		
		long start = System.currentTimeMillis();
		for (int i = 0; i < 100000; i++) {
    
    
			news += s;
		}
		long end = System.currentTimeMillis();
		
		System.out.print(end-start);
	}
}

1.2.4 常用方法

length()
charAt()
lastIndexOf()
substring()
equals()
startsWith()
endsWith()
split()
trim() 去除字符串两端的空格

1.2.5 测试

package cn.tedu.String;

import java.util.Arrays;
import java.util.Iterator;

//String的常用方法
public class Test4_String2 {
    
    
	public static void main(String[] args) {
    
    
		String s = "abcdefghijk";
		
		System.out.println(s.charAt(3));//d,返回指定索引处的 char 值。
		System.out.println(s.endsWith("jk"));//true,是否以指定后缀结束
		
		String s1 = "abc";
		System.out.println(s.equals(s1));//false,判断两个字符串的内容是否相同
		System.out.println(s.length());//11,字符串的长度
		
		String s2="1,2,3,4,5";
		String[]  strs = s2.split(",");//根据指定的规则切割字符串
		System.out.println(Arrays.toString(strs));//查看数组元素
		System.out.println(s2.startsWith("1,"));//true
		//截取时含头不含尾[3,7)
		System.out.println(s.substring(3,7));//defg
		//把指定的参数转换成String类型
		//把数字10转成字符串10
		System.out.println(String.valueOf(10));
		
	}
}

1.3 StringBuilder/StringBuffer

1.3.1 特点

1、 封装了char[]数组
2、 是可变的字符序列
3、 提供了一组可以对字符内容修改的方法
4、 常用append()来代替字符串做字符串连接
5、 内部字符数组默认初始容量是16:initial capacity of 16 characters
6、 如果大于16会尝试将扩容,新数组大小原来的变成2倍+2,容量如果还不够,直接扩充到需要的容量大小。
int newCapacity = value.length * 2 + 2;
7、 StringBuffer 1.0出道线程安全,StringBuilder1.5出道线程不安全

1.3.2 练习:测试字符串连接

package day010;

public class Test5_SB {
    
    
	public static void main(String[] args) {
    
    
		String s="abcdefghijklmnopqrstuvwxyz";
		StringBuilder sb=new StringBuilder();
		
		long start  = System.currentTimeMillis();
		for (int i = 0; i < 100000000; i++) {
    
    
			sb.append(s);//在后面空白处,添加
		}
		long end  = System.currentTimeMillis();
		System.out.println(end-start);
		
	}
}

1.3.3 方法

append()
charAt()

1.4 包装类

1.4.1 与基本类型的对应关系

在这里插入图片描述

1.4.2 Number

数字包装类的抽象父类。
子类:
在这里插入图片描述

常用的方法:
提供了各种获取值的方式,已经完成了强转。
在这里插入图片描述

1.4.3 Integer

创建对象

	new Integer(5);新建对象
	Integer.valueOf(5);

在Integer类中,包含256个Integer缓存对象,范围是 -128到127。
使用valueOf()时,如果指定范围内的值,访问缓存对象,而不新建;如果指定范围外的值,直接新建对象。

Integer a = new Integer(5);//创建对象
Integer b = Integer.valueOf(5);//读取缓存
Integer c = Integer.valueOf(5);//读取缓存
System.out.println(b==c);//true
System.out.println(a==b);//false
System.out.println(a.equals(b));//true

方法
parseInt();字符串转换成int
toBinaryString();把整数转换成2进制数据
toOctalString();把整数转换成8进制数据
toHexString();把整数转换成16进制数据

1.4.4 Double

创建对象

new Double(3.14)
Double.valueOf(3.14)//和 new 没有区别

方法

Double.parseDouble();

1.5 日期类Date

1.5.1 概述

存在于java.util.Date包。
用来封装一个毫秒值表示一个精确的时间点。
从1970-1-1 0点开始的毫秒值。

1.5.2 创建对象

new Date():封装的是系统当前时间的毫秒值
new Date(900000000000L):封装指定的时间点

1.5.3 常用方法

getTime():取内部毫秒值
setTime():存取内部毫秒值
getMonth():获取当前月份
getHours():获取当前小时
compareTo(Date):当前对象与参数对象比较。当前对象大返回正数,小返回负数,相同0

1.5.4 练习1:测试日期类的常用方法

package cc;
import java.util.Date;
public class bb {
    
    
	public static void main(String[] args) {
    
    
		Date d = new Date();
		System.out.println(d);//输出当前时间
		System.out.println(d.getTime());//输出1970-1-1至今的毫秒值
		System.out.println(d.getMonth());//获取当前月份
		System.out.println(d.getHours());//获取当前小时
	}
}

1.6 日期工具SimpleDateFormat

1.6.1 概述

日期格式化工具,可以把Date对象格式化成字符串,也可以日期字符串解析成Date对象。

1.6.2 创建对象

new SimpleDateFormat(格式)
格式:yyyy-MM-dd HH:mm:ss
MM/dd/yyyy..

1.6.3 常见方法

format(Date):把Date格式化成字符串
parse(String):把String解析成Date

1.6.4 练习1 :计算存活天数

接收用户输入的出生日期,计算存活天数

package cc;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
public class bb {
    
    
	public static void main(String[] args) throws ParseException {
    
    
//生存XX天
		String birth = new Scanner(System.in).nextLine();
		SimpleDateFormat s =new SimpleDateFormat("yyyy-MM-dd");
		Date date = s.parse(birth);
		long birthday = date.getTime();
		long now=System.currentTimeMillis();
		System.out.println((now-birthday)/1000/60/60/24);
	}
}

1.7 拓展

1.7.1 进制

概念
进制也就是进位计数制,是人为定义的带进位的计数方法,类似于统计“正”字。
对于任何一种进制—X进制,就表示每一位置上的数运算时都是逢X进一位。
十进制是逢十进一,十六进制是逢十六进一,二进制就是逢二进一,以此类推。
通常情况下,1byte=8个二进制位
所以表示一个数字用二进制来表示的话就可以这样表示:0000 0000
把这8个位进行组合,每三位组合就形成了八进制,每四位组合就形成了十六进制。
特点
二进制:0和1,逢二进一,以0b开始
八进制:0-7,逢八进一,以0开始
十进制:0-9,逢十进一
16进制:0-9,abcdef,逢16进一,以0x开始

进制的转化:
十进制转二进制:不断除以2商0为止,取余,倒着写。
把十进制11转成2进制:1011。
在这里插入图片描述

二进制转十进制:从低位次,每位乘以2的位次次幂 再求和。
计算二进制数据:0000 1101对应的十进制
计算二进制数据:0110 1110对应的十进制
在这里插入图片描述

二进制转八进制:从低位次开始,每三位为一组,产生一个八进制数字,最高位不足补零。
计算二进制数据110 0111对应的八进制
在这里插入图片描述

八进制转二进制:把一个数字转为3个数字,不足三位的,最高位补零。
计算八进制数据:023 0653对应的二进制数据
在这里插入图片描述

二进制转十六进制:四个一组,转为1个数字,以0x开始
略。。。
十六进制转二进制:一个数字变成4个数字
略。。。

1.7.2 StringBuilder和StringBuffer的区别

1、 在线程安全上,
–StringBuffer是旧版本就提供的,线程安全的。@since JDK1.0
–StringBuilder是jdk1.5后产生,线程不安全的。@since 1.5
2、 在执行效率上,StringBuilder > StringBuffer > String
3、 源码体现:本质上都是在调用父类抽象类AbstractStringBuilder来干活,只不过Buffer把代码加了同步关键字,使得程序可以保证线程安全问题。

abstract class AbstractStringBuilder implements Appendable, CharSequence {
    
    }

在这里插入图片描述
在这里插入图片描述

1.7.3 自动装箱和自动拆箱

自动装箱:把基本类型包装成一包装类的对象

Integer a = 5;//a是引用类型,引用了包装对象的地址。
编译器会完成对象的自动装箱:Integer a = Integer.valueOf(5);

自动拆箱:从包装对象中,自动取出基本类型值

	int i = a;//a现在是包装类型,没法给变量赋值,需要把5取出来。
编译器会完成自动拆箱:int i = a.intValue();

2 IO1

2.1 BigDecimal/BigInteger

2.1.1 概述

BigDecimal:常用来解决精确的浮点数运算。
BigInteger:常用来解决超大的整数运算。

2.1.2 创建对象

BigDecimal.valueOf(2);

2.1.3 常用方法

add(BigDecimal bd): 做加法运算
substract(BigDecimal bd) : 做减法运算
multiply(BigDecimal bd) : 做乘法运算
divide(BigDecimal bd) : 做除法运算
divide(BigDecimal bd,保留位数,舍入方式):除不尽时使用
setScale(保留位数,舍入方式):同上
pow(int n):求数据的几次幂

2.1.4 练习1:测试常用方法

接收用户输入的两个数字,做运算。

package seday11;
import java.math.BigDecimal;
import java.util.Scanner;
public class Test2_BigD {
    
    
	public static void main(String[] args) {
    
    
		double a = new Scanner(System.in).nextDouble();
		double b = new Scanner(System.in).nextDouble();
		System.out.println(a+b);
		System.out.println(a-b);
		System.out.println(a*b);
		System.out.println(a/b);//不精确
		
		System.out.println(===上面的除法不精确===);
		BigDecimal bd1 = BigDecimal.valueOf(a);
		BigDecimal bd2 = BigDecimal.valueOf(b);
		BigDecimal bd3;
		bd3=bd1.add(bd2);
		System.out.println(bd3.doubleValue());
		
		bd3=bd1.subtract(bd2);
		System.out.println(bd3.doubleValue());
		
		bd3=bd1.multiply(bd2);
		System.out.println(bd3.doubleValue());
		
//		bd3=bd1.divide(bd2);//报错除不尽
		//保留位数和舍入方式
		bd3=bd1.divide(bd2,5,BigDecimal.ROUND_HALF_UP);
		bd3=bd3.setScale(2, BigDecimal.ROUND_HALF_UP);//保留两位
		System.out.println(bd3.doubleValue());
		
	}
}

2.2 IO简介

2.2.1 继承结构

in/out相对于程序而言的输入(读取)和输出(写出)的过程。
在Java中,根据处理的数据单位不同,分为字节流和字符流

字节流:针对二进制文件

InputStream 
--FileInputStream
--BufferedInputStream 
--ObjectInputStream
OutputStream
--FileOutputStream
--BufferedOutputStream
--ObjectOutputStream

字符流:针对文本文件。读写容易发生乱码现象,在读写时最好指定编码集为utf-8

Writer
--BufferedWriter
--OutputStreamWriter
Reader
--BufferedReader
--InputStreamReader
--PrintWriter/PrintStream

2.2.2 流的概念

数据的读写抽象成数据,在管道中流动。
 流只能单方向流动
 输入流用来读取in
 输出流用来写出Out
 数据只能从头到尾顺序的读写一次

2.3 File文件流

2.3.1 概述

封装一个磁盘路径字符串,对这个路径可以执行一次操作。
可以用来封装文件路径、文件夹路径、不存在的路径。

2.3.2 创建对象

File(String pathname) 
          通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。

2.3.3 常用方法

文件、文件夹属性


length():文件的字节量
exists():是否存在,存在返回true
isFile():是否为文件,是文件返回true
isDirectory():是否为文件夹,是文件夹返回true
getName():获取文件/文件夹名
getParent():获取父文件夹的路径
getAbsolutePath():获取文件的完整路径


创建、删除
createNewFile():新建文件,文件夹不存在会异常,文件已经存在返回false
mkdirs():新建多层不存在的文件夹\a\b\c
mkdir():新建单层不存在的文件夹\a
delete():删除文件,删除空文件夹
文件夹列表
	list():返回String[],包含文件名
	listFiles():返回File[],包含文件对象

2.3.4 练习1:测试常用方法

创建day10工程
创建cn.tedu.io包
创建Test1.java

package cn.tedu.io;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;

import org.junit.Test;

//测试文件类
public class Test1_File {
    
    
	//Junit单元测试方法:
	//@Test + public + void + 没有参数
	@Test
	public void show() throws IOException {
    
    
		//1,创建File对象,读取了指定位置的文件
		File f = new File("D:\\teach\\a");
		
		//文件夹列表list() listFiles()
		String[] names = f.list();
		System.out.println(Arrays.toString(names));
		
		File[] files = f.listFiles();//推荐,更常见
		System.out.println(Arrays.toString(files));
		
		
		//TODO常用方法
		System.out.println(f.createNewFile());//新建文件,文件夹不存在会异常,文件已经存在返回false
		System.out.println(f.mkdir());//新建单层不存在的文件夹
		System.out.println(f.mkdirs());//新建多层不存在的文件夹
		System.out.println(f.delete());//删除文件,删除空文件夹
		
		System.out.println();
		
		System.out.println(f.length());//文件的字节量
		System.out.println(f.exists());//是否存在,存在返回true
		System.out.println(f.isFile());//是否为文件,是文件返回true
		System.out.println(f.isDirectory());//是否为文件夹,是文件夹返回true
		System.out.println(f.getName());//获取文件/文件夹名
		System.out.println(f.getParent());//获取父文件夹的路径
		System.out.println(f.getAbsolutePath());//获取文件的完整路径
		
	}
}

2.3.5 练习2:递归求目录总大小

递归:不断的调用方法本身。
递归:统计文件大小,删除文件
求目录的总大小:
1、把指定目录封装成File对象
2、把文件夹列表列出来
3、判断,如果是文件,直接把f.length()相加
4、判断,如果是文件夹,继续列表,继续判断,如果是文件相加,如果又是文件夹,继续列表,继续判断,如果是文件相加…
5、如果是文件夹,递归调用方法本身的业务逻辑

package cn.tedu.io;
import java.io.File;

import org.junit.Test;
//递归求目录总大小
public class Test2_File2 {
    
    
	public static void main(String[] args) {
    
    
//		1、把指定目录封装成File对象
		File file = new File("D:\\teach\\a");
		int size =count(file);
		System.out.println(size);
	}
	
	private static int count(File file) {
    
    
//		2、把文件夹列表列出来
		File[] files = file.listFiles();
		
		//2.1 遍历数组里的每个资源
		int sum = 0;//记录文件的大小
		for (int i = 0; i < files.length; i++) {
    
    
//		3、判断,如果是文件,直接把f.length()相加 
// files[i]表示每次遍历到的资源
				if(files[i].isFile()) {
    
     
					sum += files[i].length();//求文件的和
				}else if(files[i].isDirectory()){
    
    
//		4、判断,如果是文件夹,继续列表,继续判断,如果是文件相加,如果又是文件夹,继续列表,继续判断,如果是文件相加......
//		5、如果是文件夹,递归调用方法本身的业务逻辑
					sum += count(file[i]);//把当前遍历到的文件夹继续循环判断求和
				}
		}
		
		return sum ;
	}
	
	
}

2.3.6 练习3:递归删除文件夹

2.4 字节流读取

字节流是由字节组成的,字符流是由字符组成的. Java里字符由两个字节组成.字节流是最基本的,所有的InputStream和OutputStream的子类都是,主要用在处理二进制数据。
流式传输主要指将整个音频和视频及三维媒体等多媒体文件经过特定的压缩方式解析成一个个压缩包,由视频服务器向用户计算机顺序或实时传送。在采用流式传输方式的系统中,用户不必像采用下载方式那样等到整个文件全部下载完毕,而是只需经过几秒或几十秒的启动延时即可在用户的计算机上利用解压设备对压缩的A/V、3D等多媒体文件解压后进行播放和观看。此时多媒体文件的剩余部分将在后台的服务器内继续下载。

2.4.1 InputStream抽象类

此抽象类是表示字节输入流的所有类的超类/抽象类。
常用方法:

abstract  int read() 
          从输入流中读取数据的下一个字节。 
 int read(byte[] b) 
          从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。 
 int read(byte[] b, int off, int len) 
          将输入流中最多 len 个数据字节读入 byte 数组。 
void close() 
          关闭此输入流并释放与该流关联的所有系统资源。

2.4.2 FileInputStream子类

直接插在文件上,直接读取文件数据。
创建对象

FileInputStream(File file) 
          通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。 
FileInputStream(String pathname) 
          通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。

2.4.3 BufferedInputStream子类

BufferedInputStream 为另一个输入流添加一些功能,即缓冲输入以及支持 mark 和 reset 方法的能力。在创建 BufferedInputStream 时,会创建一个内部缓冲区数组(默认8M大小)。在读取或跳过流中的字节时,可根据需要从包含的输入流再次填充该内部缓冲区,一次填充多个字节。
创建对象

BufferedInputStream(InputStream in) 
          创建一个 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。

2.5 字符流读取

常用于处理纯文本数据。

2.5.1 Reader抽象类

用于读取字符流的抽象类。
常用方法:

int read() 
          读取单个字符。 
int read(char[] cbuf) 
          将字符读入数组。 
abstract  int read(char[] cbuf, int off, int len) 
          将字符读入数组的某一部分。 
 int read(CharBuffer target) 
          试图将字符读入指定的字符缓冲区。 
abstract  void close() 
          关闭该流并释放与之关联的所有资源。

2.5.2 InputStreamReader子类

InputStreamReader 是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。

创建对象

InputStreamReader(InputStream in, String charsetName) 
          创建使用指定字符集的 InputStreamReaderInputStreamReader(InputStream in) 
          创建一个使用默认字符集的 InputStreamReader

2.5.3 FileReader子类

用来读取字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是适当的。要自己指定这些值,可以先在 FileInputStream 上构造一个 InputStreamReader。
创建对象

FileReader(String fileName) 
          在给定从中读取数据的文件名的情况下创建一个新 FileReaderFileReader(File file) 
          在给定从中读取数据的 File 的情况下创建一个新 FileReader

2.5.4 BufferedReader子类

从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了。

创建对象

BufferedReader(Reader in) 
          创建一个使用默认大小输入缓冲区的缓冲字符输入流。

2.6 综合练习:文件的读取

读取指定文件

package cn.tedu.hello;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;

public class tt {
    
    
	public static void main(String[] args) throws Exception {
    
    
		method1();// 字节读取
		method2();//字符读取
	}

	private static void method2() throws Exception {
    
    
		//字符流读图片乱码
//		BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(new File("D:\\teach\\1.jpg"))));
		BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(new File("D:\\teach\\a\\1.txt"))));
//		System.out.println(in.readLine());
//		System.out.println(in.readLine());//null读到/n/r
		
		String line = "";
		while((line = in.readLine())!=null) {
    
    //一行一行读
			System.out.println(line);
		}
		
		in.close();
	}

	private static void method1() throws Exception {
    
    
		long s = System.currentTimeMillis();
		InputStream in = new FileInputStream("D:\\teach\\1.jpg");
		int b = 0;
		while ((b = in.read()) != -1) {
    
    
			// System.out.println(b);
		}
		s = System.currentTimeMillis() - s;
		System.out.println(s + "--");// 7515

		long ss = System.currentTimeMillis();
		InputStream in2 = new BufferedInputStream(new FileInputStream("D:\\teach\\1.jpg"));
		int b2 = 0;
		while ((b2 = in2.read()) != -1) {
    
    
			// System.out.println(b2);
		}
		ss = System.currentTimeMillis() - ss;
		System.out.println(ss + "==");// 32
		
		in.close();
		in2.close();
	}
}

2.7 扩展1

2.7.1 字符流和字节流的区别

2.7.2 字符流读写乱码

new BufferedReader(new InputStreamReader(?,”utf-8));
new BufferedWriter(new OutputStreamWriter(?,”utf-8));
//默认是系统的编码,GBK写出。
//如果打开和写出的编码用的表不一致,会造成乱码。
OutputStreamWriter os = new OutputStreamWriter(new FileOutputStream("encode.txt"),"utf-8");
os.write("中国");
os.flush();

2.7.3 常见字符编码表

在这里插入图片描述

测试

@Test
public void code() throws IOException {
    
    
	String s = "我爱你中国";
	System.out.println(s.getBytes("utf-8").length);//15--unicode/u8一个汉字3字节存储
	System.out.println(s.getBytes("gbk").length);//10--中文双字节
	System.out.println(s.getBytes("unicode").length);//12--双字节+2
	System.out.println(s.getBytes("iso-8859-1").length);//5--单字节
}

2.7.4 JDK1.7新特性之IO关流

try(    ){
    
       }catch(){
    
     }

private static void customBufferStreamCopy(File source, File target) {
    
    
    InputStream fis = null;
    OutputStream fos = null;
    try {
    
    
        fis = new FileInputStream(source);
        fos = new FileOutputStream(target);
  
        byte[] buf = new byte[8192];
  
        int i;
        while ((i = fis.read(buf)) != -1) {
    
    
            fos.write(buf, 0, i);
        }
    }
    catch (Exception e) {
    
    
        e.printStackTrace();
    } finally {
    
    
        close(fis);
        close(fos);
    }
}
  
private static void close(Closeable closable) {
    
    
    if (closable != null) {
    
    
        try {
    
    
            closable.close();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }
}

上述代码对于异常处理十分复杂,
对于资源的关闭也很麻烦,那么可以和下面的进行对比:

private static void customBufferStreamCopy(File source, File target) {
    
    
    try (InputStream fis = new FileInputStream(source);
        OutputStream fos = new FileOutputStream(target)){
    
    
  
        byte[] buf = new byte[8192];
  
        int i;
        while ((i = fis.read(buf)) != -1) {
    
    
            fos.write(buf, 0, i);
        }
    }
    catch (Exception e) {
    
    
        e.printStackTrace();
    }
}

3 IO2

3.1 字节流写出

3.1.1 OutputStream抽象类

此抽象类是表示输出字节流的所有类的超类。输出流接受输出字节并将这些字节发送到某个接收器。
常用方法:

void close() 
          关闭此输出流并释放与此流有关的所有系统资源。 
 void flush() 
          刷新此输出流并强制写出所有缓冲的输出字节。 
 void write(byte[] b) 
          将 b.length 个字节从指定的 byte 数组写入此输出流。 
 void write(byte[] b, int off, int len) 
          将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。 
abstract  void write(int b) 
          将指定的字节写入此输出流。

3.1.2 FileOutputStream子类

直接插在文件上,直接写出文件数据
创建对象:

FileOutputStream(String name) 
          创建一个向具有指定名称的文件中写入数据的输出文件流。FileOutputStream(File file) 
          创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
FileOutputStream(File file, boolean append) –追加
          创建一个向指定 File 对象表示的文件中写入数据的文件输出流。

3.1.3 BufferedOutputStream子类

该类实现缓冲的输出流。通过设置这种输出流,应用程序就可以将各个字节写入底层输出流中,而不必针对每次字节写入调用底层系统。
创建对象

BufferedOutputStream(OutputStream out) 
创建一个新的缓冲输出流,以将数据写入指定的底层输出流。

3.2 字符流写出

3.2.1 Writer抽象类

写入字符流的抽象类。
常用方法:

void write(char[] cbuf) 
          写入字符数组。 
abstract  void write(char[] cbuf, int off, int len) 
          写入字符数组的某一部分。 
 void write(int c) 
          写入单个字符。 
 void write(String str) 
          写入字符串。 
 void write(String str, int off, int len) 
          写入字符串的某一部分。
abstract  void close() 
          关闭此流,但要先刷新它。

3.2.2 OutputStreamWriter子类

OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。

创建对象

OutputStreamWriter(OutputStream out, String charsetName) 
          创建使用指定字符集的 OutputStreamWriterOutputStreamWriter(OutputStream out) 
          创建使用默认字符编码的 OutputStreamWriter

3.2.3 FileWriter子类

用来写入字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的。要自己指定这些值,可以先在 FileOutputStream 上构造一个 OutputStreamWriter。
创建对象

FileWriter(String fileName) 
          根据给定的文件名构造一个 FileWriter 对象。
FileWriter(String fileName, boolean append) 
          根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。

3.2.4 BufferedWriter子类

将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。 可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够大了。

创建对象

BufferedWriter(Writer out) 
          创建一个使用默认大小输出缓冲区的缓冲字符输出流。

3.3 综合练习:文件的写出

把数据写出到指定文件中。如果文件不存在会自动创建,文件夹不存在会报错。

package cn.tedu.hello;

import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;

public class rr {
    
    
	public static void main(String[] args) throws Exception {
    
    
//		method1();//字节写出
		method2();//字符写出
	}

	private static void method2() throws Exception {
    
    
		Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File("D:\\\\teach\\\\a.txt"))
																											, "utf-8"));
		long s = System.currentTimeMillis();
		for(int i = 48 ; i < 1000000; i++) {
    
    
			out.write(i);
		}
		s = System.currentTimeMillis() - s;
		System.out.println(s + "--");//266 
		
		out.close();
	}

	private static void method1() throws Exception {
    
    
		long s = System.currentTimeMillis();
		OutputStream out = new FileOutputStream(new File("D:\\teach\\a.txt"));
		for(int i = 48 ; i < 1000000; i++) {
    
    
			out.write(i);
		}
		s = System.currentTimeMillis() - s;
		System.out.println(s + "--");//3484 
		
		long ss = System.currentTimeMillis();
		OutputStream out2 = new BufferedOutputStream(new FileOutputStream(new File("D:\\teach\\a2.txt")));
		for(int i = 48 ; i < 1000000; i++) {
    
    
			out2.write(i);
		}
		ss = System.currentTimeMillis() - ss;
		System.out.println(ss + "==");//54 
		
		out.close();
		out2.close();
	}
}

3.4 IO综合练习

3.4.1 练习1:文件复制

from,to。读取from的数据。写出到to文件里

package cn.tedu.io;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

//文件复制
public class Test4_Copy {
    
    
	public static void main(String[] args) throws Exception {
    
    
		// 1,创建读取文件和写出文件
		File from = new File("D:\\teach\\a\\1.txt");
		File to = new File("D:\\teach\\a\\to.txt");
//调用copy完成文件复制
		copy(from, to);
	}

	//封装了文件复制的工具,将来可以通过类名.直接调用
	public static void copy(File from, File to) throws Exception {
    
    
		// 2,读取from,写出到to
		InputStream in = new FileInputStream(from);
		OutputStream out = new FileOutputStream(to);

		// 3,开始读,读到-1为止
		int b = 0;// 记录每次读取到的数据
		while ((b = in.read()) != -1) {
    
    
			out.write(b);// 把读到的内容写出去
		}

		// 4,关闭资源
		in.close();
		out.close();
	}
}

3.4.2 练习2:批量读写

package cn.tedu.io;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

//文件复制
public class Test4_Copy {
    
    
	public static void main(String[] args) throws Exception {
    
    
		// 1,创建读取文件和写出文件
		File from = new File("D:\\teach\\a\\1.txt");
		File to = new File("D:\\teach\\a\\to.txt");

		copyByte(from, to);// 一个字节一个自己的复制
		copyArray(from, to);// 一个数组一个数组的复制

	}

	// 一个数组一个数组的复制
	private static void copyArray(File from, File to) throws Exception {
    
    
		// 2,读取from,写出到to
		InputStream in = new FileInputStream(from);
		OutputStream out = new FileOutputStream(to);

		// 3,批量的读和写
		int b = 0;// 记录每次读取到的数据
		//源码:数组默认的长度一般是8M数组的长度就是8*1024
		byte[] bs = new byte[8*1024];//用来缓存数据
		while ((b = in.read(bs)) != -1) {
    
    //读取数组中的内容
			out.write(bs);// 把读到的数组里的内容写出去
		}

		// 4,关闭资源
		in.close();
		out.close();

	}

	// 封装了文件复制的工具,将来可以通过类名.直接调用
	public static void copyByte(File from, File to) throws Exception {
    
    
		// 2,读取from,写出到to
		InputStream in = new FileInputStream(from);
		OutputStream out = new FileOutputStream(to);

		// 3,开始读,读到-1为止
		int b = 0;// 记录每次读取到的数据
		while ((b = in.read()) != -1) {
    
    
			out.write(b);// 把读到的内容写出去
		}

		// 4,关闭资源
		in.close();
		out.close();
	}
}

3.5 序列化 / 反序列化

3.5.1 概述

序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。
在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。
序列化:利用ObjectOutputStream,对象的信息,按固定格式转成一串字节值输出并持久保存到磁盘化。
反序列化:利用ObjectInputStream,读取磁盘中序列化数据,重新恢复对象。

3.5.2 特点/应用场景

1、 需要序列化的文件必须实现Serializable接口以启用其序列化功能。
2、 不需要序列化的数据可以被修饰为static的,由于static属于类,不随对象被序列化输出。
3、 不需要序列化的数据也可以被修饰为transient临时的,只在程序运行期间,在内存中存在不会被序列化持久保存。
4、 在反序列化时,如果和序列化的版本号不一致时,无法完成反序列化。
5、 每个被序列化的文件都有一个唯一id,如果没有添加编译器会根据类的定义信息计算产生一个版本号。
6、 常用于服务器之间的数据传输,序列化成文件,反序列化读取数据。
7、 常用于使用套接字流在主机之间传递对象。

3.5.3 ObjectOutputStream

ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。可以使用 ObjectInputStream 读取(重构)对象。通过在流中使用文件可以实现对象的持久存储。

ObjectOutputStream(OutputStream out) 
          创建写入指定 OutputStreamObjectOutputStreamvoid writeObject(Object obj) 
          将指定的对象写入 ObjectOutputStream

3.5.4 ObjectInputStream

ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。

ObjectInputStream(InputStream in) 
          创建从指定 InputStream 读取的 ObjectInputStreamObject readObject()ObjectInputStream 读取对象,读取序列化数据。

3.5.5 练习1:将学生信息序列化至磁盘【序列化】

package cn.tedu.serializable;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class Test5_Seri {
    
    
	public static void main(String[] args) throws Exception, IOException {
    
    
		//序列化:就是把java对象保存在磁盘中
		ObjectOutputStream os = 
						new ObjectOutputStream(
								new FileOutputStream(
										"D:\\teach\\a\\student.txt"));
		
		Student s = new Student("张三",20,"成都");
		os.writeObject(s);
		
		os.close();//关闭输出资源
		
		//反序列化:从磁盘读到程序里
		ObjectInputStream in = 
				new ObjectInputStream(
						new FileInputStream(
								"D:\\teach\\a\\student.txt"));
		
		//读到的对象,默认是Object,需要强转成子类
		Student s2 = (Student)in.readObject();
		System.out.println(s2);
		
	}
}
//1,如果想完成序列化,类必须实现Serializable接口
//只是用来做标记,需要序列化
class  Student implements Serializable{
    
    
	//创建对象用
	public Student(String name, int age, String addr) {
    
    
		this.name = name;
		this.age = age;
		this.addr = addr;
	}
	//一般序列化的都是属性
	String name = "张三";
	int age = 20;
	String addr = "成都";
	
	//为了看属性值
	@Override
	public String toString() {
    
    
		return "Student [name=" + name + ", age=" + age + ", addr=" + addr + "]";
	}
	
}

3.6 编码转换流

用来作为桥梁,把字节流转成字符流的桥梁。
用来解决字符流读写乱码问题。

3.6.1 工具类

OutputStreamWriter:是字节流通向字符流的桥梁

--OutputStreamWriter(OutputStream out, String charsetName)
--OutputStreamWriter(OutputStream out)     

InputStreamReader:是字节流通向字符流的桥梁

--InputStreamReader(InputStream in)
--InputStreamReader(InputStream in, String charsetName)

3.6.2 常见字符编码表

在这里插入图片描述

3.6.3 测试

package cn.tedu.decode;
 
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
 
//编码转换流的测试
 
public class Test5_Decode {
    
    
    public static void main(String[] args) {
    
    
//     method();//输出的转换流
       method2();//读取的转换流
    }
 
    private static void method2() {
    
    
       try {
    
    
           //1,创建转换对象
           //第二个参数可以设置编码表,解决乱码现象
           //文件保存时使用的是gbk编码,如果用utf-8就会乱码
//         BufferedReader in = new BufferedReader(
//                      new InputStreamReader(
//                      new FileInputStream("1.txt"),"utf-8"));//乱码
           BufferedReader in = new BufferedReader(
                  new InputStreamReader(
                         new FileInputStream("1.txt"),"gbk"));
          
           //2,读取一行数据
           String line = in.readLine();//子类BufferedReader的特有方法
           System.out.println(line);
          
           //3,释放资源
          
       }catch(Exception e) {
    
    
           e.printStackTrace();
       }
    }
 
    private static void method() {
    
    
       try {
    
    
           // 1,创建转换流输出对象,OutputStreamWriter是字节流通向字符流的桥梁
           //第二个参数可以设置编码表,解决乱码现象
           //写出去的文件如果使用了utf-8表,打开时,使用了默认的gbk编码表,就会出现乱码
//         OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream("1.txt"), "utf-8");
           OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream("1.txt"), "gbk");
 
           // 2,开始写出数据
           // 当数据的保存方式和打开方式,使用的不是一张表时,就会出现乱码!!
           out.write("大家好,我是渣渣辉");
 
           // TODO 3,释放资源
           out.flush();
 
       } catch (Exception e) {
    
    
           e.printStackTrace();
       }
    }
 
}

3.7 扩展

3.7.1 IO中flush()和close()的区别

3.7.2 封装释放资源的close()

public static void close(Closeable io) {
    
    
		if (io != null) {
    
    
			try {
    
    
				io.close();
			} catch (IOException e) {
    
    
				e.printStackTrace();
			}
		}
}

3.7.3 BIO、NIO、AIO的区别

阻塞IO,BIO 就是传统的 java.io 包,它是基于流模型实现的,交互的方式是同步、阻塞方式,也就是说在读入输入流或者输出流时,在读写动作完成之前,线程会一直阻塞在那里,它们之间的调用时可靠的线性顺序。它的有点就是代码比较简单、直观;缺点就是 IO 的效率和扩展性很低,容易成为应用性能瓶颈。
非阻塞IO,NIO 是 Java 1.4 引入的 java.nio 包,提供了 Channel、Selector、Buffer 等新的抽象,可以构建多路复用的、同步非阻塞 IO 程序,同时提供了更接近操作系统底层高性能的数据操作方式。
异步IO,AIO 是 Java 1.7 之后引入的包,是 NIO 的升级版本,提供了异步非堵塞的 IO 操作方式,所以人们叫它 AIO(Asynchronous IO),异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。但目前还不够成熟,应用不多。

3.7.4 数组和链表区别

List是一个接口,它有两个常用的子类,ArrayList和LinkedList,看名字就可以看得出一种是基于数组实现另一个是基于链表实现的。
数组ArrayList遍历快,因为存储空间连续;链表LinkedList遍历慢,因为存储空间不连续,要去通过指针定位下一个元素,所以链表遍历慢。
数组插入元素和删除元素需要重新申请内存,然后将拼接结果保存进去,成本很高。例如有100个值,中间插入一个元素,需要数组重新拷贝。而这个动作对链表来说,太轻松了,改变一下相邻两个元素的指针即可。所以链表的插入和修改元素时性能非常高。
实际开发就根据它们各自不同的特点来匹配对应业务的特点。业务一次赋值,不会改变,顺序遍历,就采用数组;业务频繁变化,有新增,有删除,则链表更加适合。

3.7.5 读一行写一行

package game;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.PrintWriter;

//测试
public class aa {
    
    
	public static void main(String[] args) {
    
    
		try {
    
    

BufferedReader in = new BufferedReader(new FileReader("1.txt"));
			String line = in.readLine();
			System.out.println(line);//没数据就会读到null


	BufferedReader in = new BufferedReader(new FileReader("1.txt"));
			PrintWriter out = new PrintWriter("2.txt");
		
			String line;
			while ( (  line=in.readLine()  ) != null) {
    
    
				out.println(line);
			}
			out.flush();
			
		} catch (Exception e) {
    
    
			e.printStackTrace();
		}
	}
	
}

4 泛型+集合1

4.1 泛型

4.1.1 概念

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable{
    
    }
public interface Deque<E> extends Queue<E> {
    
    }
public interface Queue<E> extends Collection<E> {
    
    }
public interface Collection<E> extends Iterable<E> {
    
    }

我们上面的代码中出现的<?>是什么东西呢 它叫泛型,常用来和集合对象一同使用,所以我们在开始学习集合之前,必须先了解下什么是泛型。而且泛型概念非常重要,它是程序的增强器,它是目前主流的开发方式。
泛型是(Generics)是JDK1.5 的一个新特性,其实就是一个『语法糖』,本质上就是编译器为了提供更好的可读性而提供的一种小手段,小技巧,虚拟机层面是不存在所谓『泛型』的概念的。

4.1.2 作用

 通过泛型的语法定义,约束集合元素的类型,进行安全检查,把错误显示在编译期
 代码通用性更强,后面有案例
 泛型可以提升程序代码的可读性,但它只是一个语法糖(编译后这样的东西就被删除,不出现在最终的源代码中),对于JVM运行时的性能是没有任何影响的。

4.1.3 泛型示例

在这里插入图片描述

我们创建一个ArrayList,上面看到eclipse提示有个黄线,什么意思呢?

ArrayList is a raw type. References to generic type ArrayList<E> should be parameterized. 

ArrayList使用了泛型,在声明时需指定具体的类型。
那我们把这个<>里的方式就称为泛型。上面的泛型有什么作用呢?就是在编译阶段就检查我们传入的参数类型是否正确。
在这里插入图片描述

有了泛型,我们可以看到人家要求存放String,而我故意存放的整数100,所以eclipse提示我们错误:

The method add(int, String) in the type List<String> is not applicable for the arguments (int)

类型List的add方法要求增加的类型为String类型,不正确不能存入。

4.1.4 泛型声明

泛型可以在接口、方法、返回值上使用:
java.util.List泛型接口/类:

public interface Collection<E> {
    
    }

泛型方法的声明:

public <E> void print(E e) {
    
    }

在方法返回值前声明了一个表示后面出现的E是泛型,而不是普通的java变量。

4.1.5 常用名称

 E - Element (在集合中使用,因为集合中存放的是元素)
 T - Type(Java 类)
 K - Key(键)
 V - Value(值)
 N - Number(数值类型)
 ? - 表示不确定的java类型

4.1.6 用途:编译时类型检查

package seday12new;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Test1 {
    
    
	public static void main(String[] args) {
    
    
		int[] a = new int[3];
		a[0]=1;
		a[1]=2;
		//int类型的数组,规定了数组里的数据类型,类型不对就报错。
//		a[2]="hello";
		
		//1,泛型的标志<>
		//2,泛型的好处:规定了数据的类型,不能想放什么数据就放什么类型,要遵守泛型规定的类型
		//3,泛型的数据类型只能是引用类型,不能是基本类型
		List<Integer> list = new ArrayList<Integer>();
		list.add(1);
		list.add(2);
//4,如果类型不对,把运行时期才会 报的错ClassCastException直接在编译时期就报出来
//		list.add("a");
//		list.add('b');
		
		Iterator it = list.iterator();
		while(it.hasNext()) {
    
    
			Integer s = (Integer) it.next();
			System.out.println(s);
		}
		
	}
}

4.1.7 用途:代码通用性更强

**传统方式:**通过重载多态实现,方法同名,参数类型不同。

package javase.base.gennarics;
 
public class TestOldStyle {
    
    
    public static void print(Integer[] dArray) {
    
    
       for( Integer d : dArray) {
    
    
           System.out.println(d);
       }
    }
   
    public static void print( String[] sArray) {
    
    
       for( String s : sArray) {
    
    
           System.out.println(s);
       }
    }
   
    public static void main(String[] args) {
    
    
       Integer[] scores = new Integer[]{
    
    100,98,80};
       String[] names = new String[]{
    
    "语文","数学","英语"};
      
       TestOldStyle.print(scores);
       TestOldStyle.print(names);
    }
}

泛型方式

package javase.base.gennarics;
 
public class TestGenarics {
    
    
    public static <E> void print(E[] arr) {
    
    
       for(E e : arr) {
    
    
           System.out.println(e);
       }
    }
   
    public static void main(String[] args) {
    
    
       Integer[] scores = new Integer[]{
    
     100,98,80 };
       String[] names = new String[]{
    
     "语文","数学","英语" };
       Double[] moneys = new Double[] {
    
     10.1,20.2,30.3 };
      
       TestGenarics.print(scores);
       TestGenarics.print(names);
       TestGenarics.print(moneys);
    }
}

4.1.8 类型擦除

泛型只是在编译期间生存,编译后就被干掉了,真正运行时,大多情况下取而代之的是Object。
下面的代码利用了jdk提供的强大的反射功能,后续会专门详细讲解,今天先初体验下其强大的功能。

package javase.generics;
 
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
 
//泛型类型擦除
public class TestGenerics {
    
    
    public static void main(String[] args) throws Exception {
    
    
       List<Integer> list = new ArrayList<Integer>();
      
       //1. 编译器按泛型检查,类型报错。这是在编译阶段
       //list.add("chenzs");
      
       //2. 但在实际运行时,泛型的地方就被替代为通用类型Object
       Class<?> clazz = list.getClass();
       Method m = clazz.getDeclaredMethod("add", Object.class);
      
       //3. 利用发射得到的对象是运行时对象,其就可以设置非整形的数据
       m.invoke(list, "chenzs");
      
      
       System.out.println(list.get(0));
    }
}

4.2 Collection接口

4.2.1 概述

英文名称Collection,是用来存放对象的数据结构。其中长度可变,而且集合中可以存放不同类型的对象。并提供了一组操作成批对象的方法。
数组的缺点:长度是固定不可变的,访问方式单一,插入、删除等操作繁琐。

4.2.2 集合的继承结构

在这里插入图片描述

Collection接口
-- List接口  : 数据有序,可以重复。
   -- ArrayList子类
   -- LinkedList子类
-- Set接口  : 数据无序,不可以存重复值
   -- HashSet子类
-- Map接口  : 键值对存数据
-- HashMap
Collections工具类

4.2.3 常用方法

boolean add(E e):添加元素。
boolean addAll(Collection  c):把小集合添加到大集合中 。
boolean contains(Object o) : 如果此 collection 包含指定的元素,则返回 trueboolean isEmpty() :如果此 collection 没有元素,则返回 trueIterator<E> iterator():返回在此 collection 的元素上进行迭代的迭代器。 
boolean remove(Object o) :从此 collection 中移除指定元素的单个实例。
int size() :返回此 collection 中的元素数。
Objec[] toArray():返回对象数组

4.2.4 练习1:测试常用方法

package seday11;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Test_301_Collection {
    
    
	public static void main(String[] args) {
    
    
		Collection c = new ArrayList();//接口无法直接创建对象
		c.add("hello");//添加元素
		c.add("java");//添加元素
		c.add("~");//添加元素
		c.add(10);//jdk5后有自动装箱功能,相当于把10包装Integer.valueOf(10)
		System.out.println(c.remove("~"));//移除元素
		System.out.println(c.contains("a"));//判断包含关系
		System.out.println(c.size());//集合的长度
		System.out.println(c);
		
		//for遍历集合
		for(int i =0 ;i<c.size();i++) {
    
    
			System.out.println(c.toArray()[i]);
		}
		//iterator迭代器遍历
		Iterator it = c.iterator();//对 collection 进行迭代的迭代器
		while (it.hasNext()) {
    
    //如果仍有元素,则返回 true
			System.out.println(it.next());//返回迭代获取到的下一个元素
		}   } }

4.3 List接口

4.3.1 概述

有序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。

4.3.2 特点

1、 数据有序
2、 允许存放重复元素
3、 元素都有索引

4.3.3 常用方法

ListIterator<E> listIterator()
          返回此列表元素的列表迭代器(按适当顺序)。
 ListIterator<E> listIterator(int index)
          返回列表中元素的列表迭代器(按适当顺序),从列表的指定位置开始。
 void add(int index, E element)
          在列表的指定位置插入指定元素(可选操作)。
 boolean addAll(int index, Collection<? extends E> c)
          将指定 collection 中的所有元素都插入到列表中的指定位置(可选操作)。
 List<E> subList(int fromIndex, int toIndex)
     返回列表中指定的 fromIndex(包括 )和 toIndex(不包括)之间的部分视图。
E get(int index)
          返回列表中指定位置的元素。  
int indexOf(Object o)
          返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1

4.3.4 练习1:测试常用方法

创建day12工程
创建cn.tedu.list包
创建Test1_List.java

package cn.tedu.list;
 
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
 
//这类用来测试List接口的常用方法
public class Test1_List {
    
    
 
    public static void main(String[] args) {
    
    
       //1、创建List对象
       //特点1:List集合元素都有索引,可以根据索引直接定位元素
       List list = new ArrayList();
      
       //2、常用方法
       list.add(111);
       list.add(222);
       list.add(333);
       list.add(444);
       list.add('a');
       list.add("abc");
      
       list.add(3,666);//在3的索引处添加指定元素
 
       //特点2:元素有序, 怎么存就怎么放
       System.out.println(list);//[111, 222, 333, 666, 444, a, abc]
      
       Object obj = list.get(4);//get(m)-m是索引值,获取指定索引位置的元素
       System.out.println(obj);
      
      
       //3、迭代/遍历集合中的元素
       //使用Collection接口提供的iterator()
       Iterator it = list.iterator();
       while(it.hasNext()) {
    
    //判断集合里有没有下个元素
           Object o = it.next();
//         System.out.println(o);
       }
      
       //使用List接口提供的listIterator()
       //interfaceListIterator extends Iterator
       //区别:可以使用父接口的功能,同时拥有自己的特有功能,不仅顺序向后迭代还可以逆向迭代
       ListIterator it2 = list.listIterator();
       while(it2.hasNext()) {
    
    
           Object o = it2.next();
//         System.out.println(o);
       }
      
       System.out.println();
      
       List list2 = list.subList(1, 3);//subList(m,n)-m是开始索引,n是结束索引,其中含头不含尾类似于String.subString(m,n)
       System.out.println(list2);
      
    }
   
}

4.4 ArrayList

4.4.1 概述

  1. 存在于java.util包中。
  2. 内部用数组存放数据,封装了数组的操作,每个对象都有下标。
  3. 内部数组默认初始容量是10。如果不够会以1.5倍容量增长。
  4. 查询快,增删数据效率会降低。
    在这里插入图片描述
    在这里插入图片描述

4.4.2 创建对象

new ArrayList():初始容量是10

4.4.3 练习1:测试常用方法

常用API,包括下标遍历,迭代器遍历

package seday11;
import java.util.ArrayList;
public class Test3_AL {
    
    
	public static void main(String[] args) {
    
    
		ArrayList list = new ArrayList();
		list.add("aaa");//存入数据
		list.add("123");
		list.add("ccc");
		list.add("ddd");
		System.out.println(list);//list中内容
		System.out.println(list.size());//集合长度
		System.out.println(list.get(1));//根据下标获取元素
			
		System.out.println();
		System.out.println(list.remove(2));//移除下标对应的元素
		System.out.println(list);
			
			//下标遍历
			for (int i = 0; i < list.size(); i++) {
    
    
				System.out.print(list.get(i));
			}

	//Iterator迭代遍历,封装了下标
			Iterator<String> it = list.iterator();
			while (it.hasNext()) {
    
    //如果有数据
				String s = it.next();//一个一个向后遍历
				System.out.println(s);
			}	
		}
}

4.5 LinkedList

4.5.1 概述

双向链表,两端效率高。底层就是数组和链表实现的。

4.5.2 常用方法

add()
get()
size()
remove(i)
remove(数据)
iterator()
addFirst()  addLast()
getFirst()  getLast()
removeFirst()  removeLast()

4.5.3 练习1:测试迭代器遍历

双向链表:下标遍历效率低,迭代器遍历效率高

package dd;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;

public class tt {
    
    
	public static void main(String[] args) throws Exception {
    
    
		LinkedList ll = new LinkedList ();
		for (int i = 0; i < 100000; i++) {
    
    
			ll.add(100);
		}
		f1(ll);
		f2(ll);
	}

	private static void f2(LinkedList<Integer> ll) {
    
    
		long t = System.currentTimeMillis();
		Iterator it = ll.iterator();
		while(it.hasNext()) {
    
    
			it.next();
		}
		t = System.currentTimeMillis()-t;
		System.out.println("=====iterator========="+t);//16
	}

	private static void f1(LinkedList<Integer> ll) {
    
    
		long t = System.currentTimeMillis();
		for (int i = 0; i < ll.size(); i++) {
    
    
			ll.get(i);
		}
		long t1 = System.currentTimeMillis();
		System.out.println("~~~~for~~~~~~~"+(t1-t));//9078
	}
}

4.6 扩展

4.6.1 ArrayList扩容

ArrayList相当于在没指定initialCapacity时就是会使用延迟分配对象数组空间,当第一次插入元素时才分配10(默认)个对象空间。假如有20个数据需要添加,那么会分别在第一次的时候,将ArrayList的容量变为10 (如下图一);之后扩容会按照1.5倍增长。也就是当添加第11个数据的时候,Arraylist继续扩容变为10*1.5=15(如下图二);当添加第16个数据时,继续扩容变为15 * 1.5 =22个
ArrayList没有对外暴露其容量个数,查看源码我们可以知道,实际其值存放在elementData对象数组中,那我们只需拿到这个数组的长度,观察其值变化了几次就知道其扩容了多少次。怎么获取呢?只能用反射技术了。

4.6.2 HashMap扩容

成长因子:
static final float DEFAULT_LOAD_FACTOR = 0.75f;
前面的讲述已经发现,当你空间只有仅仅为10的时候是很容易造成2个对象的hashcode 所对应的地址是一个位置的情况。这样就造成 2个 对象会形成散列桶(链表)。这时就有一个加载因子的参数,值默认为0.75 ,如果你hashmap的 空间有 100那么当你插入了75个元素的时候 hashmap就需要扩容了,不然的话会形成很长的散列桶结构,对于查询和插入都会增加时间,因为它要一个一个的equals比较。但又不能让加载因子很小,如0.01,这样显然是不合适的,频繁扩容会大大消耗你的内存。这时就存在着一个平衡,jdk中默认是0.75,当然负载因子可以根据自己的实际情况进行调整。

5 集合2

5.1 Set接口

5.1.1 概述

一个不包含重复元素的 collection。
数据无序(因为set集合没有下标)。
由于集合中的元素不可以重复。常用于给数据去重。

5.1.2 特点

 HashSet:底层是哈希表,包装了HashMap,相当于向HashSet中存入数据时,会把数据作为K,存入内部的HashMap中。当然K仍然不许重复。
 TreeSet:底层就是TreeMap,也是红黑树的形式,便于查找数据。
 HashMap实现中,当哈希值相同的对象,会在同一个hash值的位置存储不同属性的数据。

5.1.3 常用方法

boolean add(E e):添加元素。
boolean addAll(Collection  c):把小集合添加到大集合中 。
boolean contains(Object o) : 如果此 collection 包含指定的元素,则返回 trueboolean isEmpty() :如果此 collection 没有元素,则返回 trueIterator<E> iterator():返回在此 collection 的元素上进行迭代的迭代器。 
boolean remove(Object o) :从此 collection 中移除指定元素的单个实例。
int size() :返回此 collection 中的元素数。
Objec[] toArray():返回对象数组

5.1.4 练习1:测试常用方法

package seday12;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class Test0_Map {
    
    
	public static void main(String[] args) {
    
    
		Set set = new HashSet ();
		set.add("hello");
		set.add("b");
		set.add("a");
		set.add("world");
		set.add("b");

		//不存重复元素,元素无序
		System.out.println(set);
		
		//迭代器
		Iterator it = set.iterator();
		while(it.hasNext()) {
    
    
			System.out.println(it.next());
		}
	}
}

5.2 HashSet

5.2.1 概述

此类实现 Set 接口,由哈希表(实际上是一个 HashMap 实例)支持。它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变。此类允许使用 null 元素。

5.2.2 练习1:获取HashSet里的元素

package seday12;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class Test0_Map {
    
    
	public static void main(String[] args) {
    
    
		HashSet set = new HashSet();
		set.add("a");
		set.add("e");
		set.add("b");
		set.add("a");
		set.add("b");
		
		System.out.println(set);//无序,不重复
		
		Iterator it = set.iterator();
		while(it.hasNext()) {
    
    
			System.out.println(it.next());
		}
		
	}
}

5.2.3 练习2:Set存储属性值相同的对象

需求:我们仍然假设相同属性的两个人是同一个人
1、按照以前的经验,这种需求只需要重写equals()方法就可以实现。
2、但是我们提供以后,equals()根本就没有执行。问题出现在新增功能。
3、查找新增的源码发现,其实在添加时只是计算对象的hash值。
4、由于每次创建对象时hash值都不一样,所以每次都会当做新对象存起来。
5、所以,现在我们必须保证两个对象的hash值相同,重写hashCode()。

package seday12;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class Test0_Map {
    
    
	public static void main(String[] args) {
    
    
		HashSet set = new HashSet();
		//创建元素
		Student s1 = new Student("西门庆",20);
		Student s2 = new Student("武大郎",19);
		Student s3 = new Student("潘金莲",21);
		Student s4 = new Student("小龙女",23);
		Student s5 = new Student("武大郎",19);
		Student s6 = new Student("潘金莲",21);
		//添加时,新元素会和老元素比
		set.add(s1);
		set.add(s2);
		set.add(s3);
		set.add(s4);
		//默认:添加时查找对象的hash值,没有查到就存起来
		//所以必须让hash值一致才可以
		set.add(s5);
		set.add(s6);
		//问题1:属性相同时还是认为是两个对象...
		System.out.println(set);
		
//		遍历
		Iterator it = set.iterator();
		while(it.hasNext()) {
    
    
			System.out.println(it.next());
		}
		
	}
}

创建Studentpackage seday12;

public class Student {
    
    
	private String name;
	private int age;
	
	public Student(String name, int age) {
    
    
		this.name = name;
		this.age = age;
	}
	public Student() {
    
    
	}
	public String getName() {
    
    
		return name;
	}
	public void setName(String name) {
    
    
		this.name = name;
	}
	public int getAge() {
    
    
		return age;
	}
	public void setAge(int age) {
    
    
		this.age = age;
	}
	
	@Override
	public String toString() {
    
    
		return "Student [name=" + name + ", age=" + age + "]";
	}
	
	//需求:属性值都一样就看做是同一个对象
	@Override
	public boolean equals(Object obj) {
    
    
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Student other = (Student) obj;
		if (age != other.age)
			return false;
		if (name == null) {
    
    
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}
	
	//HashSet默认:添加时查找对象的hash值,没有查到就存起来
	//所以必须让hash值一致才可以
//必须用算法,不然的话,hash值相同时会挂一串
	public int hashCode() {
    
    
		//return 0;//效率低
//让基本类型*31,引用类型就用自己的hash值
	   final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}
	
}

5.3 Map接口

5.3.1 概述

java.util接口 Map<K,V>
类型参数: K - 此映射所维护的键的类型V - 映射值的类型。
也叫哈希表、散列表。常用于存 键值对 结构的数据。其中的键不能重复,值可以重复.
在这里插入图片描述

5.3.2 特点

 可以根据键 提取对应的值
 键不允许重复,如果重复值会被覆盖
 存放的都是无序数据
 初始容量是16,默认的加载因子是0.75
在这里插入图片描述

5.3.3 继承结构

在这里插入图片描述

5.3.4 常用方法

void clear() 
          从此映射中移除所有映射关系(可选操作)。 
 boolean containsKey(Object key) 
          如果此映射包含指定键的映射关系,则返回 trueboolean containsValue(Object value) 
          如果此映射将一个或多个键映射到指定值,则返回 trueV get(Object key) 
          返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 nullboolean isEmpty() 
          如果此映射未包含键-值映射关系,则返回 trueV put(K key, V value) 
          将指定的值与此映射中的指定键关联(可选操作)。 
 void putAll(Map<? extends K,? extends V> m) 
          从指定映射中将所有映射关系复制到此映射中(可选操作)。 
 V remove(Object key) 
          如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。 
 int size() 
          返回此映射中的键-值映射关系数。 
Set<Map.Entry<K,V>> entrySet() 
          返回此映射所包含的映射关系的 Set 视图。

5.3.5 练习1:测试常用方法

package seday12;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;


public class Test0_Map {
    
    
	public static void main(String[] args) {
    
    
		Map map = new HashMap ();
		map.put("001", "钢铁侠");
		map.put("002", "蜘蛛侠");
		map.put("003", "绿巨人");
		map.put("004", "灭霸");
		map.put("005", "美国队长");
		map.put("005", "凤姐");
		System.out.println(map.containsKey("001"));
		System.out.println(map.containsValue("美国队长"));
		System.out.println(map.isEmpty());
		System.out.println(map.get("003"));
		System.out.println(map.remove("001"));
		System.out.println(map.size());
		Map map2 = new HashMap ();
		map2.put("999", "刘德华");
map.put(null,null);//可以存入键为null,值也null的数据

		map.putAll(map2);
		System.out.println(map);
		
		//keySet()返回键的set集合,把map的key形成set集合
		Set set = map.keySet();
		System.out.println(set);
		
		
		//map集合的遍历,
		//方式1:keySet():把map中的可以放入set集合
		//遍历方式1:keySet ()
Set set = m.keySet();
		Iterator it = set.iterator();
		while(it.hasNext()) {
    
    
			String key = (String) it.next();
			String val = (String) m.get(key);
			System.out.println(key+"="+val);
		}
	
		//遍历方式2:entrySet ()
Set set2 = m.entrySet();
		Iterator it2 = set2.iterator();
		while(it2.hasNext()) {
    
    
			Entry en = (Entry) it2.next();
			String key = (String) en.getKey();
			String value = (String) en.getValue();
			System.out.println(key+"=="+value);
		}
		
	}
}

5.4 HashMap

 HashMap的键要同时重写hashCode()和equals()
hashCode()用来判断确定hash值是否相同
equals()用来判断属性的值是否相同
– equals()判断数据如果相等,hashCode()必须相同
– equals()判断数据如果不等,hashCode()尽量不同

5.4.1 概述

基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。
HashMap底层是一个Entry数组,当存放数据时会根据hash算法计算数据的存放位置。算法:hash(key)%n,n就是数组的长度。
当计算的位置没有数据时,就直接存放,当计算的位置有数据时也就是发生hash冲突的时候/hash碰撞时,采用链表的方式来解决的,在对应的数组位置存放链表的头结点。对链表而言,新加入的节点会从头结点加入。

5.4.2 练习1:读取HashMap的数据

package seday12;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class Test0_Map {
    
    
	public static void main(String[] args) {
    
    
		HashMap map = new HashMap ();
		map.put(100, "刘德华");
		map.put(101, "梁朝伟");
		map.put(102, "古天乐");
		map.put(103, "周润发");

//遍历方式1:keySet ()
Set set = m.keySet();
		Iterator it = set.iterator();
		while(it.hasNext()) {
    
    
			String key = (String) it.next();
			String val = (String) m.get(key);
			System.out.println(key+"="+val);
		}
	
		//遍历方式2:entrySet ()
Set set2 = m.entrySet();
		Iterator it2 = set2.iterator();
		while(it2.hasNext()) {
    
    
			Entry en = (Entry) it2.next();
			String key = (String) en.getKey();
			String value = (String) en.getValue();
			System.out.println(key+"=="+value);
		}
	}
}

5.4.3 练习2:字符串中的字符统计

接收用户输入的一串字符串,统计出现的每个字符的个数

package seday12;
import java.util.HashMap;
import java.util.Scanner;
public class Test2_Count {
    
    
	public static void main(String[] args) {
    
    
		//abacbcda
		String s = new Scanner(System.in).nextLine();
		
		//a 1  b 2  c 1
		HashMap<Character,Integer> map = new HashMap<>();
		//遍历字符串获取每个字符
		for(int i = 0;i<s.length();i++) {
    
    
			//1,取出字符串中的每个字符
			char c = s.charAt(i);
			
			//拿着字符查个数
			Integer count = map.get(c);

			//如果取出来是null,就存1,
			if(count==null) {
    
    
				map.put(c, 1);
			}else {
    
    
				//如果取出来有值,计数加1
				map.put(c,count+1);
			}
		}
		System.out.println(map);
	}
}

5.5 Collections工具类

5.5.1 常用方法

Collections.sort(List<> list):根据元素的自然顺序 对指定列表按升序进行排序。
Collections.max():根据元素的自然顺序,返回给定 collection 的最大元素。
Collections.min():根据元素的自然顺序 返回给定 collection 的最小元素。
Collections.swap(List,i,j):在指定列表的指定位置处交换元素。
Collections.addAll()

5.5.2 测试

package seday12;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Test4_Collections {
    
    
	public static void main(String[] args) {
    
    
		List<String> list = new ArrayList();
		
		//添加多个元素
		Collections.addAll(list, 
				"3","30","23","15","29","12","26");
		
		//元素排序
		Collections.sort(list);
	   //默认是字符顺序:[12,15,23,26,29, 3, 30]
		System.out.println(list);
		
		//自己定义比较方式
		Collections.sort(list, new Comparator<String>() {
    
    
			//自定义比较器,sort()自动调用
			@Override
			public int compare(String o1, String o2) {
    
    
				//把字符串转成int比大小
				int a = Integer.parseInt(o1);
				int b = Integer.parseInt(o2);
//o1大是正数,o1小是负数,相等是0
				return a-b;
			}
		});
		System.out.println(list);
	}
}

5.6 扩展

5.6.1 集合和数组的区别

5.6.2 HashMap实现原理

猜你喜欢

转载自blog.csdn.net/u012932876/article/details/122837914