java基础语法


typora-copy-images-to: resource
typora-root-url: resource

基础篇

字节

定义:字节是我们常见的计算机中小存储单元。计算机存储任何的数据,都是以字节的形式存储。

**位(bit):**一个数字0或者一个数字1,代表一位。

**字节(Byte):**每逢8位是一个字节,这是数据存储的最小单位。

8个bit(二进制位) 0000-0000表示为1个字节,写成1 byte或者1 B。

8 bit = 1 B 1024 B =1 KB 1024 KB =1 MB 1024 MB =1 GB 1024 GB = 1 TB

编译和运行的区别

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BWpfIzqi-1603932138193)(/clipboard-1602635655993.png)]

**编译:**是指将我们编写的Java源文件翻译成JVM认识的class文件,在这个过程中, javac 编译器会检查我们所写的程序是否有错误,有错误就会提示出来,如果没有错误就会编译成功。

**运行:**是指将 class文件 交给JVM去运行,此时JVM就会去执行我们编写的程序了。

标识符

定义:是指在程序中,我们自己定义内容。比如类的名字、方法的名字和变量的名字等等,都是标识符。

命名规则: 硬性要求

标识符可以包含 英文字母26个(区分大小写) 、 0-9数字 、 $(美元符号) 和 _(下划线) 。

标识符不能以数字开头。

标识符不能是关键字。

命名规范: 软性建议

类名规范:首字母大写,后面每个单词首字母大写(大驼峰式)。

方法名规范: 首字母小写,后面每个单词首字母大写(小驼峰式)。

变量名规范:全部小写。

字符常量:(单引号引起来,只能写一个字符,必须有内容) ‘a’ , ‘好’

字符串常量 :(双引号引起来,可以写多个字符,也可以不写) “A” ,“Hello” ,“你好” ,""

基本数据类型(1块空间)

数据类型关键字内存占用取值范围
字节型byte1个字节-128~127
短整型short2个字节-32768~32767
整型int(默认)4个字节-2^31~-2147483647
长整型long8个字节-263~263-1
单精度浮点数float4个字节1.4013E-45~3.4028E+38(小数点前后7位有效数字)
双精度浮点数double(默认)8个字节4.9E-324~1.7977E+308 (小数点前后16位有效数字)
字符型char2个字节0-65535(实际上是一个16位无符号整数,只能保存单个字符:‘a’,‘3’,‘你’,可以保存转义字符:‘\b’,’\t’,’\r’,’\n’,Unicode表示法是在值前加前缀\u:\u002A(米))
布尔类型boolean1个字节true,false

注意:

​ 1.字符串、数组、类、接口、Lambda是引用类型,占两块空间

​ 2.浮点数当中默认类型是double。如果一定要使用float类型,需要加上一个后缀F。

​ 3.如果是整数,默认为int类型,如果一定要使用long类型,需要加上一个后缀L。推荐使用大写字母后缀。

​ 4.java里无%lf,无论double还是float,都是%f。

Scanner 接收输入

import java.util.Scanner; //导入Scanner工具,以便使用。
Scanner input = new Scanner(System.in);
//录入String型数据
String brand = input.next();//空格和回车都可以作为录入操作的终止符
String brand = input.nextLine();//只有回车可以终止录入
//录入int型数据
int count = input.nextInt();
//录入float型
float price = input.nextFloat();
//录入double型数据
double radius = input.nextDouble();
//录入char型数据
char sex = input.next().charAt(0);//获取用户输入字符串的第一个字符

类型转换

非赋值运算自动转换规则

byte、short、char–>int–>long–>float–>doble

1)如操作数之一为double,则另一个操作数先被转化为double,再参与算术运算。

2)如两操作数均不为double,当操作数之一为float,则另一操作数先被转换为float,再参与运算。

3)如两操作数均不为double或float,当操作数之一为long,、则另一操作数先被转换为long,再参与算术运算。

4)如两操作数均不为double、float或long,则两操作数先被转换为int,再参与运算。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aeUlo85S-1603932138194)(/clipboard.png)]

short a = 1;
byte b = 2;
char c = 3;
//三个输出都是 class java.lang.Integer
System.out.println(b+a);//输出3
System.out.println(b+c);//输出5
System.out.println(a+c);//输出4
//当运算符为取正运算符(+)。取负运算符(-)或按位取反运算符(~)时,
//如果操作数为byte、char或short,则先被转换为int,再参与运算
赋值运算自动转换规则

将小范围类型变量转换成大范围类型的变量(注意不是按字节大小转换)

long d = 333;   float e = d; //可以
short a = 1; char b = a; //不行,因为取值范围不是包含,而是交叉
int a = 1; byte b = a; //不行,大的转成小的,要强制类型转换,byte b = (byte) a;
long num = 2147483648;//编译出错,因默认数字为Int类型
long num = 2147483648L;//这样才对
强制转换

将取值范围大的类型 强制转换成 取值范围小的类型。

浮点转成整数,直接取消小数点,可能造成数据损失精度。

int 强制转成 short 砍掉2个字节,可能造成数据丢失。

short s = 32767;  //定义s为short范围内最大值 
s = (short)(s + 10);// 运算后,强制转换,砍掉2个字节后会出现不确定的结果 

ASCII表:0:48,A:65,a:97

***一个方法里不能再定义一个方法

//+=符号的拓展
short s = 1;
s+=1;  
System.out.println(s); //2

分析: s += 1 逻辑上看作是 s = s + 1 计算结果被提升为int类型,再向short类型赋值时发生错误,因为不能将取值范围大的类型赋值到取值范围小的类型。但是, s=s+1进行两次运算 , += 是一个运算符,只运算一次,并带有强制转换的特点, 也就是说 s += 1 就是 s = (short)(s + 1) ,因此程序没有问题编译通过,运行结果是2.

//对于byte/short/char三种类型转换
//1.如果右侧的值没有超过左边类型的范围,编译器会自动加上强转
byte num1 = 30; // 编译器自动转换:int --> short
//2.如果右侧的值超过了左边类型的范围,那么直接编译器报错
byte num2 = 128; //报错

char zifu = 65; //编译器自动转换:int -->char
char zifu2 = -32; //报错

short a = 8;
short b = 5;
short c = a + b;//报错,会有数值丢失,需要强转
short d = 8 + 5;//不会报错,编译之后,得到的class文件中相当于直接就是------short d = 13直接就给你计算好了<--常量优化
//常量和变量运算区别的拓展
byte b1=1;
byte b2=2;  
byte b3=1 + 2;   
byte b4=b1 + b2;  
System.out.println(b3);  
System.out.println(b4); 

分析: b3 = 1 + 2 , 1 和 2 是常量,为固定不变的数据,在编译的时候(编译器javac),已经确定了 1+2 的结果并没 有超过byte类型的取值范围,可以赋值给变量 b3 ,因此 b3=1 + 2 是正确的。反之, b4 = b2 + b3 , b2 和 b3 是变量,变量的值是可能变化的,在编译的时候,编译器javac不确定b2+b3的结果是什 么,因此会将结果以int类型进行处理,所以int类型不能赋值给byte类型,因此编译失败。

***打印语句中的+号

System.out.println("a"+"b");//ab   字符串连接
System.out.println("a"+20);//a20   任务数据类型和字符串连接,都会变成字符串
System.out.println("a"+20+30);//a2030   优先级问题,从左到右执行
System.out.println("a"+(20+30));//a50   加括号提升了优先级

***用final来定义常量,定义了就不允许再修改了

***switch后面小括号里可以写byte/short/char/int/String/enum

***对于一个没有返回值的方法,可以写return;(返回其本身,此时return代表的是结束方法)

内存划分

栈(stack):存放的都是方法中的局部变量,方法一定要再栈中运行

局部变量:方法的参数,或者是方法{}内部的变量

作用域:一旦超出作用域,立刻从栈内存当中消失

堆(heap):凡是new出来的东西,都在堆当中。

堆内存里面的东西都有一个地址道:16进制

堆内存中的数据,都有默认值:int:0 double/float:0.0 char:’\u0000’ boolean:false string:null

方法区(method area):存储.class相关信息,包含方法的信息

本地方法栈(native method stack):与操作系统相关

寄存器(pc register):与cpu相关

类中的成员变量没有赋值,将会有默认值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zJfNTPxK-1603932138196)(/clipboard-1602636039351.png)]

方法执行的时候要进栈,在栈中,main方法在最底部,其他在main方法上面,进栈之后,执行结束就出栈了,继续执行main方法。还有类中成员变量在堆区有赋值的直接给值,而成员方法,却是保留了方法区中的成员方法的地址。

下面是new了两个实例的内存情况

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AHpNx29C-1603932138198)(/clipboard-1602636168005.png)]

局部变量没有默认值,成员变量有默认值。局部变量跟着方法走,在栈区。成员变量,位于堆内存,生命周期随对象诞生,随对象被垃圾回收而消失。

方法中的参数是局部变量,并且在调用方法时,该局部变量一定会被赋值。

当方法的局部变量和类成员变量重名时,就近原则,会优先用局部变量。

如果没有写构造方法,编译器会给一个没有参数的构造方法,如果写了,就不给。

数组

**静态初始化:**int[] num = new int[]{1,2,3};

int[] num = {1,2,3}; -->省略格式的初始化就不能再拆分成两个步骤了

**动态初始化:**int[] num = new int[3];

拆分成两个步骤:int[] num; num = new int[3]; -其中的元素会拥有默认值

System.out.println(num);//直接打印数组名称,得到的是数组对应的内存地址的哈希值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oZn9KW45-1603932138199)(/clipboard-1602635712548.png)]

**数组长度:**数组名称.length;(程序运行期间,长度不可改变)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AvXaZMgB-1603932138200)(/clipboard-1602635889720.png)]

数组工具类Arrays

public static String toString(数组):将参数数组变成字符串,按照默认格式“[元素1,元素2]”

public staic void sort(数组):按照默认升序对数组元素进行排序

备注:如果是自定义的类型,该类需要有Comparable或者Comparator接口的支持

int[] array1 = {10,20};
String str1 = Arrays.toString(array1);
sout(str1);//[10,20]
int[] array2 = {2,1,3};
Array.sort(array2);
sout(Array.toString(array2));//[1,2,3]

Random随机数

import java.util.Random

Random r = new Random();
int num = r.nextInt()//整个int范围
int num = r.nextInt(3)//范围是左闭右开:0,1,2

String类

程序当中所有的双引号字符串,都是String类的对象。(就算没有new也照样是)

字符串的内容永不可变,正是因为字符串永不可变,所以字符串是可以共享使用的。

字符串效果上相当于char[]字符数组,但是底层原理是byte[]字节数组。

字符串的常见3+1种创建方式

三种构造方法:

public String();//创建一个空白字符串,不含有任何内容

public String(char[] array);//根据字符数组的内容,来创建对应的字符串

public String (byte[] array);// 根据字节数组的内容,来创建对应的字符串

一种直接创建:

String str = “hello”; //右边直接用双引号,没有new,也是一个字符串对象,jvm帮我new了。

String str1 = new String();//空参构造,说明字符串内容为空

char[] charArray = {'a','b','c'};
String str2 = new String(charArray);//打印abc

byte[] byteArray = {97,98,99};
String str3 = new String(byteArray);//打印abc
字符串常量池

程序当中直接写上双引号的字符串就在字符串常量池中。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xvfUCqy7-1603932138201)(/clipboard-1602636715704.png)]

字符串内容比较的方法

public boolean equals(Object ob)

public boolean equalsIgnoreCase(Object ob)

String str1 = "hello";
String str2 = "hello";
char[] charArray = {'h','e','l','l','o'};
String str3 = new String(charArray);
sout(str1.equals(str2));//true
sout(str2.equals(str3));//true
sout(str3.equals("hello"));//true
sout("hello".equals(str1));//true
//如果一个是常量,一个是变量的比较。推荐使用"常量".equals(变量)
String str4 = null;
sout("abc".equals(str4));//false
sout(str4.equals("abc"));//报错了,空指针异常
字符串当中与获取相关的常用方法

public int length():获取字符串中字符的个数

public String concat(String str):将当前字符串和参数字符串拼接成为返回值新的字符串

public char charAt(int index):获取指定索引位置的单个字符(索引从0开始)

public int indexOf(String str):查找参数字符串在本字符串当中首次出现的索引位置、如果没有返回值-1

int length = "sds".length();//获取字符串长度
String str1 = "aa";
String str2 = "bb";
String str3 = str1.concat(str2);//拼接字符串
sout(str1);//aa 原封不动
sout(str2);//bb 原封不动
sout(str3);//aabb 新的字符串
char ch = "hello".charAt(1);//e
String str4 = "helloworldhelloworld";
int index = str4.indexOf("llo");//输出2
字符串的截取方法

public String substring(int index):截取从参数位置一直到字符串末尾,返回新字符串

public String substring(int begin,int end):截取从begin到end(左闭右开),返回新字符串

字符串转换方法

public char[] toCharArray():将当前字符串拆分成字符数组作为返回值

public byte[] getBytes():获得当前字符串底层的字节数组

public String replace(charSequence oldString,charSequence newString):将所有出现的老的字符串替换为新的字符串,并将替换结果返回一个新的字符串(charSequence可以接受字符串)

char[] chars = "hello".toCharArray();
sout(chars[0]);//h
sout(chars.length);//5

byte[] bytes = "abs".getBytes();
sout(bytes[0]);//97

String str1 = "how do you do?";
String str2 = str1.replace("o","*");
sout(str1);//how do you do?
sout(str2);//h*w d* y*u d*?
字符串的分割方法

public String[] split(String regex):按照参数的规则,将字符串切分成若干个部分

注意:split方法的参数是一个正则表达式,如果按照英文句点“.”来切,必须写"\."。

String str1 = "aaa,bbb,ccc";
String[] array1 = str1.split(",");
sout(array1[1]);//bbb
String str2 = "aaa.bbb.ccc";
String[] array2 = str2.split("\\.");
sout(array2[1]);//bbb
基本类型与字符串类型之间的相互转换

1.基本类型 --> 字符串

基本类型的值 + “”

包装类静态方法toString(参数)

static String toString(int i) : 返回一个表示指定整数的String对象

String的静态方法valueOf(参数)

static String valueOf(nit i): 返回int参数的字符串表示形式

int i1 = 100;
String s1 = i1 + "";
System.out.println(s1 + 200);//100200
String s2 = Integer.toString(100);
System.out.println(s2 + 200);//100200
String s3 = String.valueOf(100);
System.out.println(s3 + 200);//100200

2.字符串 -->基本类型:

除了Character类之外,其他所有包装类都具有parseXxx静态方法可以将字符串参数转换为对应的基本类型:

- public static byte parseByte(String s):将字符串参数转换为对应的byte基本类型。

- public static short parseShort(String s):将字符串参数转换为对应的short基本类型。

- public static int parseInt(String s):将字符串参数转换为对应的int基本类型。

- public static long parseLong(String s):将字符串参数转换为对应的long基本类型。

- public static float parseFloat(String s):将字符串参数转换为对应的float基本类型。

- public static double parseDouble(String s):将字符串参数转换为对应的double基本类型。

- public static boolean parseBoolean(String s):将字符串参数转换为对应的boolean基本类型。

int i = Integer.parseInt(s1);
System.out.println("i = " + i);//100

StringBuilder类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-19Lncy52-1603932138201)(/clipboard-1602640994346.png)]

1.Java.lang.StringBuilder --字符串缓冲区,可以提高字符串的效率

2.构造方法:

StringBuilder() ,构造一个不带任何字符的字符串生成器,其初始容量为16个字符

StringBuilder(String str),构造一个字符串生成器,并初始化为指定的字符串内容

StringBuilder bu1 = new StringBuilder();//空参构造方法
System.out.println("bu1 = " + bu1);
StringBuilder bu2 = new StringBuilder("abc");//带字符串构造方法
System.out.println("bu2 = " + bu2);

3.成员方法:

public StringBuilder append(…);添加任意类型数据的字符串形式,并返回当前对象自身

public String toString(); 将当前的StringBuilder对象转换为String对象

StringBuilder bu1 = new StringBuilder();
StringBuilder bu2 = bu1.append("abc");
System.out.println("bu2 = " + bu2);//abc
System.out.println("bu1 = " + bu1);//abc
System.out.println(bu1==bu2);//true
//综上,用append无需接收塔的返回值。
//链式编程
bu1.append(true).append("中");
sout(bu1);//abctrue中

//String --> StringBuilder
String str = "hello";
sout(str);//hello
StringBuilder bu = new StringBuilder(str);
bu.append("abc");
sout(bu);//helloabc
//StringBuilder --> String
String s = bu.toString();
sout(s);//helloabc

静态static关键字

修饰变量

如果一个成员变量使用了static,那么这个变量就不再属于对象自己,而是属于所在的类,多个对象共享同一份数据,不做get和set方法,一个对象给值,其他都有了该值。建议用类名.静态变量,来进行调用赋值。

修饰方法

如果一个成员方法使用了static,那么方法不属于对象,而是属于类。

没有static的方法一定要先创建对象,才可以调用,有static的静态方法,可以直接通过类名进行调用。(一个类中自己main方法调用自己的静态方法,可以省略类名)不推荐使用对象进行调用,如果这么写了,javac也会把它翻译成由类名直接调用。

注意事项

静态变量:类名称.静态变量

静态方法:类名称.静态方法()

静态不能直接访问非静态,比如在静态方法中访问成员变量是不可行的。因为在内存中是先有的静态内容,后有的非静态内容

静态方法中不能使用this,因为this代表当前对象,通过谁调用的方法,谁就是当前对象,就是this,静态不需要对象来调用。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-etgb6oZ0-1603932138202)(clipboard-1602637423830.png)]

//静态代码块
public class 类名{
static{
	//静态代码块中的内容
}
} 

特点:当第一次用到本类时,静态代码块执行唯一的一次,静态代码块要比构造方法先执行
用途:一次性的对静态成员变量进行赋值

数学相关工具类Math

public static double abs(double num):获取绝对值

public static double ceil(double num):向上取整

public static double floor(double num):向下取整

public static long round(double num):四舍五入

Math.PI:近似圆周率

注意:double强转成为int,会自动舍弃小数位。

继承

继承的时候子类与父类中有同名的成员变量

直接通过子类对象访问成员变量,等号左边是谁,就优先用谁,没有则向上找。

间接通过成员方方法访问成员变量,该方法属于谁,就优先用谁,没有则向上找。

注意:不论重名成员变量还是方法,都只会向上找父类,不会向下找子类

重名成员变量访问扩展

局部变量: 直接写成员变量名

本类的成员变量: this.成员变量名

父类的成员变量:super.成员变量名

重写

方法名称一样,参数列表也一样。–覆盖、覆写

注意:方法前加@Override,用来检测是否是有效重写,不是则报错。

子类方法的返回值可以和父类的方法返回值不同,但是一定要【小于等于】父类中返回值的范围。比如说父类返回值是String,那么子类返回Object就不行了

子类方法的权限比喻【大于等于】父类方法中的权限修饰符(public>protected>(default)>private

重载

方法名称一样,参数列表不一样。

子类构造方法中有一个默认含有的"super()"调用,所以一定先调用父类构造,后执行子类构造。

注意系统赠送的是父类的无参构造,如果父类中没有无参构造,则会出错。必须显示用super调用父类的重载构造方法。super的父类构造调用,必须在子类构造方法的第一个语句。且只能有一个super。

super关键字的三种用法

在子类的成员方法中,访问父类的成员变量

在子类的成员方法中,访问父类的成员方法

在子类的构造方法中,访问父类的构造方法

this关键字的三种用法(super访问父类,this访问本类)

在本类成员方法中,访问本类的成员变量

在本类成员方法中,访问本类的另一个成员方法

在本类的构造方法中,访问本类的另一个构造方法

注意:this(…)调用,不能在一个构造方法中调用其自身。并且必须是构造方法中的第一条语句,且只能有唯一一个。并且this与super是同等地位,同时只能有一个存在

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IRWSdY4X-1603932138203)(/clipboard-1602637881496.png)]

继承的三个特点:单继承、多级继承(爷爷)、一个类可以被多个类继承。

abstract抽象类

1.抽象方法就是加上abstract关键字,然后去掉大括号,直接分号结束

2.抽象类:抽象方法所在的类必须是抽象类,在class前加上abstract即可,可以有构造方法。

3.抽象类中可以由普通方法,更可以没有抽象方法。

4.不能直接创建抽象类对象

5.子类必须覆盖重写父类的所有抽象方法(即去掉abstract),如果没有重写全部抽象方法,则这个子类也得是一个抽象类

interface接口

1.public interface 接口名称{ | 接口依旧是.java文件

​ //接口内容 | 编译生成的字节码仍是 .java --> .class

} |

2.到java9,接口包含的内容可以有常量、抽象方法、默认方法、静态方法、私有方法

3.抽象方法:[public] [abstract] 返回值类型 方法名(参数);

注意:前面那两个是固定的关键字,可以选择性省略,只省略一个或者都省。

4.接口的实现类implements,必须覆盖重写接口中所有的抽象方法。如果没有覆盖重写全部的抽象方法,那么这个类就得是抽象类。

5.默认方法:[public] default 返回值类型 方法名(参数){

方法体

} //为了方便接口的升级、拼接函数模型

可以通过接口实现类对象直接调用,也可以被接口实现类进行覆盖重写

6.静态方法:[public] static 返回值类型 方法名(参数){

方法体

}

不能通过接口实现类对象来调用接口当中的静态方法

正确用法:通过接口名称.静态方法

7.私有方法:就只能在本接口中自己的方法中使用

普通私有方法:解决多个默认方法之间重复代码问题

private 返回值类型 方法名称(参数){

方法体

}

静态私有方法:解决多个静态方法之间重复代码问题

private static 返回值类型 方法名称(参数){

方法体

}

8.常量(成员变量):一旦赋值,不可以修改

[public] [static] [final] 数据类型 常量名 = 数值; //可以任意省略前面的那三个关键字。并且定义时必须手动进行赋值。而且常量的名称要完全大写哦~使用下划线就行分割。

9.一个类可以实现多个接口:implements A,B

10.如果某一个类实现的两个接口中有重复的抽象方法, 那么覆盖重写一个抽象方法就好了。

​ 如果两个接口中有冲突的默认方法, 那么实现类一定要对这个默认方法进行覆盖重写。

12.一个类的直接父类和其接口中的方法冲突了,优先在实现类中使用直接父类的。

13.接口与接口之间可以实现多继承。

多态

1.父类引用指向子类对象。就是对象的向上转型,也就是多态写法

2.格式:父类名称 对象名 = new 子类名称();

接口名称 对象名 = new 实现类名称();

3.成员变量:编译看左边,运行看左边

4.成员方法:编译看左边,运行看右边(obj.父子中都有的方法 --会调用子。obj.子类特有方法,会报错。obj.父类特有方法–运行看右边子类,子类没有向上找到父类)

Fu obj = new Zi()
//访问成员方法---   看new的是谁,就优先调用谁,没有则向上找。 
obj.method();//会先在子类中找,子类中如果没有这个方法,就去父类中找。
//访问成员变量
sout(obj.num);//直接通过子类对象,等号左边是谁,就优先用谁,没有则向上找。
                //就是这个会得到父类中的成员变量

5.对象的向下转型,其实就是一个【还原】动作

格式:子类名称 对象名 = (子类名称)父类对象;

含义:将父类对象,【还原】成为本来的子类对象

Animal animal = new Cat();//本来是猫,向上转型成为动物
Cat cat = (Cat)animal; //本来是猫,已经被当做动物了,又还原成为本来的猫

注意:比如保证原来new的是猫,这样后面才能还原成猫,如果不是,就会发成异常。但是编译不会报错,异常:classcastexception

6.保证向下转型的正确性

格式:对象 instanceof 类名称

作用:得到一个boolean值,判断前面的对象能不能得到后面类型的实例

final关键字

1.四种用法:修饰类、方法、局部变量、成员变量

2.修饰类:

public final class 类名称{

//… 含义:这个类不能有任何子类

} 注意:其中的成员方法,就没办法被子类所覆盖重写了

3.修饰方法:

修饰符 final 返回值类型 方法名称(参数){

方法体 含义:该方法不能被覆盖重写

} 注意:对于类和方法来说,final和abstract 是不能同时存在的

4.修饰局部变量:

final 数据类型 变量名 = 值;

注意:不能改变该变量的值,只能赋值一次。

对于基本类型来说,不可变是变量当中的数据不可改变

对于引用类型来说,不可变是变量当中的地址值不可变,但是这个地址指向的那个对象,是可以改变的。比如说final数组,数组中的值是可以改变的。

注意可以这么写-----只能赋值一次嗷

final int num1;
num1 = 10;

5.修饰成员变量:

修饰符 final 数据类型 变量名 = 值;

成员变量具有默认值,所以可以在定义的时候,进行手动直接赋值。

也可以通过构造方法,进行间接赋值。就是在定义的时候,没有手动赋值,而是在构造方法里赋值。但要保证,每一个构造方法都对该常量进行了赋值。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xqvgQWOx-1603932138204)(/clipboard-1602639149216.png)]

四种修饰符

​ public > protected > (default) > private

同一个类(我自己) ✔ ✔ ✔ ✔

同一个包(我邻居) ✔ ✔ ✔ ✖

不同包子类(我儿子) ✔ ✔ ✖ ✖

不同包非子类(陌生人) ✔ ✖ ✖ ✖

内部类

分类:成员内部类、局部内部类(包含匿名内部类)

成员内部类

修饰符 class 外部类名称{

​ 修饰符 class 内部类名称{

​ //…

​ }

//…

注意:内用外,随意使用; 外用内,需要借助内部类对象。

会形成两个.class文件,一个外部类名.class ,一个外部类名$内部类名.class

使用:间接方式–>在外部类的方法当中,使用内部类,然后main只是调用外部类的方法

直接方式–>外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VD5HbRtC-1603932138204)(/clipboard-1602639434193.png)]

局部内部类

这个类是定义在一个方法内部的。只有当前所属方法才能使用它,出了这个方法外面就不能使用

格式:修饰符 class 外部类名称{

修饰符 返回值类型 外部类方法名称(参数){

​ class 局部内部类名称{

​ //…

​ }

}

注意:并不能在外面的main方法中通过方法.内部类,只能在方法中自己new内部类

如果希望访问所在方法的局部变量,那么这个局部变量必须是【有效final】,也就是说可以不用显示写final,但这个变量也不能改变(原因:new出来的局部内部类在堆中,局部变量跟着方法走,在栈中。方法结束后出栈,局部变量就回立即消失,但是new出来的却持续存在等垃圾回收,所以要保证是常量)

小结一下与权限修饰符的关系:

外部类:public / (default)

成员内部类:public/(default)/protected/private

局部内部类:什么都不能写,此处不写,也并不是相当于default

匿名内部类(属于局部内部类)

如果接口的实现类,或者是父类的子类,只需要使用唯一的一次,那么可使用匿名内部类。

格式:接口名称 对象名 = new 接口名称(){

//覆盖重写所有抽象方法 ---- 这个是匿名内部类,但不是匿名对象。

}; -----这个实现类没有名称。只有接口名称和对象名

对格式“new 接口名称(){…}”进行解析:new代表创建对象的动作,接口名称就是匿名内部类需要实现哪个接口,{…}是匿名内部类中的内容

格式:new 接口名称(){

//覆盖重写所有抽象方法 ---- 这个是匿名内部类,也是匿名对象。

}.method(); -----这个实现类没有名称。只有接口名称

注意:匿名内部类,在【创建对象】的时候,只能创建唯一的一次嗷

​ 匿名对象,在【调用方法】的时候,只能调用唯一一次

类的成员变量还可以是类,或者接口。方法的参数也可以是类或者接口。

Object类

1.java.lang.Object

2.Object是类层次结构的根(父类),每一个类都使用其作为超(父)类,所有对象(包括数组)都实现这个类的方法。

3.方法:String toString() 返回该对象的字符串表示,是对象堆内存地址。与直接打印对象名字一样,直接名字,就是在调用这个方法。如果在类中重写了这个方法,那么直接输出的对象名的时候,也不再是地址了。

4.方法:boolean equals(Object obj) ,指向其他某个对象与此对象是否相等。

比较的是两个对象的地址值

5.Objects类的equals方法:两个对象进行比较,防止空指针异常

public static boolean equals(Object a, Object b){

return (a==b) || (a!=null && a.equals(b));

}

时间日期相关类

Date类

1.java.util.Date

2.类Date,表示特定的瞬间,精确到毫秒(1000毫秒 = 1秒)

3.时间原点0毫秒,1970年1月1日00:00:00(英国格林威治),中国属于东八区,会把时间增加8个小时。

sout(System.currentTimeMillis());//获取当前时间到时间原点经历了多少毫秒long类型

4.构造方法

Date() 获取当前系统的日期和时间

Date(long date) :传递毫秒值,把毫秒值转换为date日期

5.成员方法:long getTime() 把日期转换为毫秒值相当于System.currentTimeMillis()—转换为毫秒值,用于计算两个时间的差值。

DateFormat类

1.java.text.DateFormat

2.日期/时间格式化的抽象类。用来在Date对象和String对象间来回转换

3.格式化:日期–>文本 解析:文本–>日期

4.成员方法:

String format(Date date) 按照指定的模式,把Date日期格式化为符合模式的字符串

Date parse(String source) throws ParseException 把符合模式的字符串,解析为Date日期

如果字符串和构造方法的格式不一样就会抛出该异常。

调用一个抛出异常的方法,必须处理该异常,要么继续抛出,要么try catch

5.DateFormat是一个抽象类,要使用其子类 SimpleDateFormat

构造方法:SimpleDateFormat(String pattern)

----用给定的模式和默认语言环境的日期格式符号构造SimpleDateFormat

----参数:传递指定的模式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u9XHZ30G-1603932138204)(/clipboard-1602640502141.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gFKzJhIy-1603932138204)(/clipboard-1602640507713.png)]

//把日期格式化为字符串
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
Date date = new Date();
String d = sdf.format(date); //按照指定模式,把date转换为字符串
sout(d);//2019年7月19日 15时26分32秒

//把字符串解析为日期
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
Date date = sdf.parse("2019年7月19日 15时26分32秒");//实际中要处理异常嗷
sout(date);//又解析为了date格式的那种日期

Calendar日历类

1.java.util.Calendar

2.抽象类,里面提供了很多操作日历字段的方法

3.static Calendar getInstance() 使用默认时区和语言环境获得一个日历子类对象

Calendar c = Calendar.getInstance(); //多态,父类对象----New子类

sout©; //不是地址,是许多值,比如说time,year,month(0-11)

4.常用方法:

public int get(int field); 返回给定日历字段的值

public void set(int field, int value); 将给定的日历字段设置为给定值

public abstract void add(int field, int amount); 根据日历规则,让给定的日历字段添加或减去指定的时间量

public Date getTime(); 返回一个表示此Calendar时间值(从历元到现在毫秒的偏移量)的Date对象

方法参数:

int field:日历类的字段,可以使用Calendar类的静态成员变量获取

public static final int YEAR = 1; //年

public static final int MONTH = 2; //月

public static final int DATE = 5; //月中某一天

public static final int DAY_OF_MONTH = 5; //月中某一天

public static final int HOUR = 10; //时

public static final int MINUTE = 12; //分

public static final int SECOND = 13; //秒

Calendar c = Calendar.getInstance();
int year = c.get(Calendar.YEAR);
sout(year);//2019

c.set(Calendar.YEAR,9999);
int year = c.get(Calendar.YEAR);
sout(year);//9999

c.add(Calendar.YEAR,-2);
int year = c.get(Calendar.YEAR);
sout(year);//9997

Date date = c.getTime();
sout(date);//Sun Aug 08 16:49:37 CST 9997

System类

1.java.lang.System

2.提供了大量静态方法,进行多种与系统有关的操作

3.常用方法:

public static long currentTimeMillis(); 返回以毫秒为单位的当前时间

public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length); 将数组中的指定数据拷贝到另一个数组中

参数:src - 源数组 ,srcPos - 源数组中的起始位置(起始索引),dest - 目标数组,destPos - 目标数据中的起始位置(起始索引),length - 要复制的数组元素数量

int[] src = {1,2,3,4,5};//定义源数组
int[] dest = {6,7,8,9,10};//定义目标数组
//要把src的前三个元素拷贝到dest的前三个位置上
sout("复制前"+Arrays.toString(dest));//[6,7,8,9,10]
System.arraycopy(src,0,dest,0,3);
sout("复制后"+Arrays.toString(dest));//[1,2,3,9,10]

包装类

1.包装类:byte-Byte,short-Short,int-Integer,long-Long,float-Float,double-Double,char-Character,boolean-Boolean

2.装箱:把基本类型的数据,包装到包装类中

构造方法:Integer(int value):构造一个新分配的Integer对象,它表示指定的int值

​ Integer(String s):构造一个新分配的Integer对象,他表示String参数所指示的int值

​ 注意:s可以是"100",但不能是诸如"a"的,这样会抛异常

静态方法:static Integer valueOf(int i): 返回一个表示指定int值的Integer实例

static Integer valueOf(String s): 返回一个表示指定String值的Integer实例

3.拆箱:在包装类中取出基本类型数据

成员方法:int intValue() 以int类型返回该Integer的值

//构造方法
Integer in1 = new Integer(1);
System.out.println("in1 = " + in1);//1
//静态方法
Integer in2 = Integer.valueOf(1);
System.out.println("in2 = " + in2);//1
Integer in3 = Integer.valueOf("123");
System.out.println("in3 = " + in3);//123
//拆箱
int i = in1.intValue();
sout(i)//1

4.自动装箱与自动拆箱

Integer in = 1;//直接把int类型的整数赋值给包装类---自动装箱
in = in + 2;//in是包装类,无法直接参与运算,需要先转换为基本类型----自动拆箱
//in + 2 就相当于in.intValue() + 2 = 3  这是一个自动拆箱
//in = in.intValue() + 2 = 3 又是一个自动装箱
System.out.println("in = " + in);

可变参数

1.可变参数:是JDK1.5之后出现的新特性

2.使用前提:

​ 当方法的参数列表数据类型已经确定,但是参数的个数不确定,就可以使用可变参数.

3.使用格式:定义方法时使用

​ 修饰符 返回值类型 方法名(数据类型…变量名){}

4.可变参数的原理:

​ 可变参数底层就是一个数组,根据传递参数个数不同,会创建不同长度的数组,来存储这些参数

​ 传递的参数个数,可以是0个(不传递),1,2…多个

4.注意事项:

​ 一个方法的参数列表,只能有一个可变参数

​ 如果方法的参数有多个,那么可变参数必须写在参数列表的末尾

/*
 定义计算(0-n)整数和的方法
 已知:计算整数的和,数据类型已经确定int
 但是参数的个数不确定,不知道要计算几个整数的和,就可以使用可变参数
 add(); 就会创建一个长度为0的数组, new int[0]
 add(10); 就会创建一个长度为1的数组,存储传递来过的参数 new int[]{10};
 add(10,20); 就会创建一个长度为2的数组,存储传递来过的参数 new int[]{10,20};
 add(10,20,30,40,50,60,70,80,90,100); 
 就会创建一个长度为10的数组,存储传递来过的
 参数new int[]{10,20,30,40,50,60,70,80,90,100};
*/
public static int add(int...arr){
   //System.out.println(arr);//[I@2ac1fdc4 底层是一个数组
   //System.out.println(arr.length);//0,1,2,10
   //定义一个初始化的变量,记录累加求和
   int sum = 0;
   //遍历数组,获取数组中的每一个元素
   for (int i : arr) {
       //累加求和
       sum += i;
    }
    //把求和结果返回
    return sum;
}

//可变参数的特殊(终极)写法--可以接收任意类型的参数
public static void method(Object...obj){}

异常

基础

1.java.lang.Throwable:类是 Java 语言中所有错误或异常的超类。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X7e28exp-1603932138205)(/clipboard-1602641928611.png)]

2.Exception:编译期异常,进行编译(写代码)java程序出现的问题

​ RuntimeException:运行期异常,java程序运行过程中出现的问题

​ 异常就相当于程序得了一个小毛病(感冒,发烧),把异常处理掉,程序可以继续执行(吃点药,继续革命工作)

3.Error:错误

​ 错误就相当于程序得了一个无法治愈的毛病(非典,艾滋).必须修改源代码,程序才能继续执行

//Exception:编译期异常,进行编译(写代码)java程序出现的问题
/*SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");//用来格式化日期
Date date = null;
   try {
       date = sdf.parse("1999-0909");//把字符串格式的日期,解析为Date格式的日期
   } catch (ParseException e) {
       e.printStackTrace();
   }
System.out.println(date);*/

//RuntimeException:运行期异常,java程序运行过程中出现的问题
/*int[] arr = {1,2,3};
//System.out.println(arr[0]);
  try {
    //可能会出现异常的代码
    System.out.println(arr[3]);
  }catch(Exception e){
    //异常的处理逻辑
  System.out.println(e);
}*/

/*
   Error:错误
   OutOfMemoryError: Java heap space
   内存溢出的错误,创建的数组太大了,超出了给JVM分配的内存
*/
//int[] arr = new int[1024*1024*1024];
//必须修改代码,创建的数组小一点
int[] arr = new int[1024*1024];
System.out.println("后续代码");

4.异常的产生过程解析:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XhhUMUpT-1603932138206)(/clipboard-1602642073485.png)]

throw关键字

作用:可以使用throw关键字在指定的方法中抛出指定的异常

使用格式:throw new xxxException(“异常产生的原因”);

注意:

​ 1.throw关键字必须写在方法的内部

​ 2.throw关键字后边new的对象必须是Exception或者Exception的子类对象

​ 3.throw关键字抛出指定的异常对象,我们就必须处理这个异常对象

​ throw关键字后边创建的是RuntimeException或者是 RuntimeException的子类对象,我们可以不处理,默认交给JVM处理(打印异常对象,中断程序)

​ throw关键字后边创建的是编译异常(写代码的时候报错),我们就必须处理这个异常,要么throws,要么try…catch

public static void main(String[] args) {
    //int[] arr = null;
    int[] arr = new int[3];
    int e = getElement(arr,3);
    System.out.println(e);
}
/*
    定义一个方法,获取数组指定索引处的元素
    参数:
        int[] arr
        int index
    以后(工作中)我们首先必须对方法传递过来的参数进行合法性校验
      如果参数不合法,那么我们就必须使用抛出异常的方式,告知方法的调用者,
      传递的参数有问题
    注意:
    NullPointerException是一个运行期异常,我们不用处理,默认交给JVM处理
   ArrayIndexOutOfBoundsException是一个运行期异常,我们不用处理,默认交给JVM处理
 */
public static int getElement(int[] arr,int index){
    /*
        我们可以对传递过来的参数数组,进行合法性校验
        如果数组arr的值是null
        那么我们就抛出空指针异常,告知方法的调用者"传递的数组的值是null"
     */
    if(arr == null){
        throw new NullPointerException("传递的数组的值是null");
    }

    /*
        我们可以对传递过来的参数index进行合法性校验
        如果index的范围不在数组的索引范围内
        那么我们就抛出数组索引越界异常,
        告知方法的调用者"传递的索引超出了数组的使用范围"
     */
  if(index<0 || index>arr.length-1){
   throw new ArrayIndexOutOfBoundsException("传递的索引超出了数组的使用范围")
  }

    int ele = arr[index];
    return ele;
}

eg:Obects类中的静态方法requireNonNull

public static T requireNonNull(T obj):查看指定引用对象不是null。

​ 源码:

​ public static T requireNonNull(T obj) {

​ if (obj == null)

​ throw new NullPointerException();

​ return obj;

​ }

public static void main(String[] args) {
    method(null);
}

public static void method(Object obj){
    //对传递过来的参数进行合法性判断,判断是否为null
    /*if(obj == null){
        throw new NullPointerException("传递的对象的值是null");
    }*/

    //Objects.requireNonNull(obj);
    Objects.requireNonNull(obj,"传递的对象的值是null");
}
声明异常throws

异常处理的第一种方式,交给别人处理

作用:

​ 当方法内部抛出异常对象的时候,那么我们就必须处理这个异常对象

​ 可以使用throws关键字处理异常对象,会把异常对象声明抛出给方法的调用者处理(自己不处理,给别人处理),最终交给JVM处理–>中断处理

​ 使用格式:在方法声明时使用

​ 修饰符 返回值类型 方法名(参数列表) throws AAAExcepiton,BBBExcepiton…{

​ throw new AAAExcepiton(“产生原因”);

​ throw new BBBExcepiton(“产生原因”);

​ …

​ }

​ 注意:

​ 1.throws关键字必须写在方法声明处

​ 2.throws关键字后边声明的异常必须是Exception或者是Exception的子类

​ 3.方法内部如果抛出了多个异常对象,那么throws后边必须也声明多个异常

​ 如果抛出的多个异常对象有子父类关系,那么直接声明父类异常即可

​ 4.调用了一个声明抛出异常的方法,我们就必须的处理声明的异常

​ 要么继续使用throws声明抛出,交给方法的调用者处理,最终交给JVM

​ 要么try…catch自己处理异常

/*
    FileNotFoundException extends IOException extends Excepiton
    如果抛出的多个异常对象有子父类关系,那么直接声明父类异常即可
 */
//public static void main(String[] args) throws 
                                //FileNotFoundException,IOException {
//public static void main(String[] args) throws IOException {
public static void main(String[] args) throws Exception {
    readFile("c:\\a.tx");

    System.out.println("后续代码");
}

/*
    定义一个方法,对传递的文件路径进行合法性判断
    如果路径不是"c:\\a.txt",那么我们就抛出文件找不到异常对象,告知方法的调用者
    注意:
        FileNotFoundException是编译异常,抛出了编译异常,就必须处理这个异常
        可以使用throws继续声明抛出FileNotFoundException这个异常对象,
        让方法的调用者处理
 */
public static void readFile(String fileName) throws 
                            FileNotFoundException,IOException{
    if(!fileName.equals("c:\\a.txt")){
        throw new FileNotFoundException("传递的文件路径不是c:\\a.txt");
    }

    /*
        如果传递的路径,不是.txt结尾
        那么我们就抛出IO异常对象,告知方法的调用者,文件的后缀名不对

     */
    if(!fileName.endsWith(".txt")){
        throw new IOException("文件的后缀名不对");
    }

    System.out.println("路径没有问题,读取文件");
}
捕获异常try…catch

try…catch:异常处理的第二种方式,自己处理异常

​ 格式:

​ try{

​ 可能产生异常的代码

​ }catch(定义一个异常的变量,用来接收try中抛出的异常对象){

​ 异常的处理逻辑,异常异常对象之后,怎么处理异常对象

​ 一般在工作中,会把异常的信息记录到一个日志中

​ }

​ …

​ catch(异常类名 变量名){

​ }finally{

​ 无论是否出现异常都会执行

​ }

​ 注意:

​ 1.try中可能会抛出多个异常对象,那么就可以使用多个catch来处理这些异常对象

​ 2.如果try中产生了异常,那么就会执行catch中的异常处理逻辑,执行完毕catch中的处理逻辑,继续执行try…catch之后的代码。如果try中没有产生异常,那么就不会执行catch中异常的处理逻辑,执行完try中的代码,继续执行try…catch之后的代码

3.finally不能单独使用,必须和try一起使用

4.finally一般用于资源释放(资源回收),无论程序是否出现异常,最后都要资源释放(IO)

try{
    //可能产生异常的代码
    readFile("d:\\a.tx");
}catch (IOException e){//try中抛出什么异常对象,catch就定义什么异常变量,
                                                      //用来接收这个异常对象
    //异常的处理逻辑,异常异常对象之后,怎么处理异常对象
    //System.out.println("catch - 传递的文件后缀不是.txt");

    /*
        Throwable类中定义了3个异常处理的方法
         String getMessage() 返回此 throwable 的简短描述。
         String toString() 返回此 throwable 的详细消息字符串。
         void printStackTrace()  
                         //JVM打印异常对象,默认此方法,打印的异常信息是最全面的
     */
    //System.out.println(e.getMessage());//文件的后缀名不对
    //System.out.println(e.toString());
              //重写Object类的toString java.io.IOException: 文件的后缀名不对
    //System.out.println(e);//java.io.IOException: 文件的后缀名不对
    e.printStackTrace();
}finally {
     //无论是否出现异常,都会执行
     System.out.println("资源释放");
}
System.out.println("后续代码");
异常处理注意事项
/*
    多个异常使用捕获又该如何处理呢?
    1. 多个异常分别处理。
    2. 多个异常一次捕获,多次处理。
    3. 多个异常一次捕获一次处理。
 */

//1. 多个异常分别处理。
* try {
    int[] arr = {1,2,3};
    System.out.println(arr[3]);//ArrayIndexOutOfBoundsException: 3
}catch (ArrayIndexOutOfBoundsException e){
    System.out.println(e);
}

try{
    List<Integer> list = List.of(1, 2, 3);
    System.out.println(list.get(3));
            //IndexOutOfBoundsException: Index 3 out-of-bounds for length 3
}catch (IndexOutOfBoundsException e){
    System.out.println(e);
}*/

//2. 多个异常一次捕获,多次处理。
/*try {
    int[] arr = {1,2,3};
    //System.out.println(arr[3]);//ArrayIndexOutOfBoundsException: 3
    List<Integer> list = List.of(1, 2, 3);
    System.out.println(list.get(3));
          //IndexOutOfBoundsException: Index 3 out-of-bounds for length 3
}catch (ArrayIndexOutOfBoundsException e){
    System.out.println(e);
}catch (IndexOutOfBoundsException e){
    System.out.println(e);
}*/

/*
    一个try多个catch注意事项:
        catch里边定义的异常变量,如果有子父类关系,
        那么子类的异常变量必须写在上边,否则就会报错
        ArrayIndexOutOfBoundsException extends IndexOutOfBoundsException
 */
/*try {
    int[] arr = {1,2,3};
    //System.out.println(arr[3]);//ArrayIndexOutOfBoundsException: 3
    List<Integer> list = List.of(1, 2, 3);
    System.out.println(list.get(3));
         //IndexOutOfBoundsException: Index 3 out-of-bounds for length 3
}catch (ArrayIndexOutOfBoundsException e){
    System.out.println(e);
}catch (IndexOutOfBoundsException e){
    System.out.println(e);
}*/

//3. 多个异常一次捕获一次处理。
/*try {
    int[] arr = {1,2,3};
    //System.out.println(arr[3]);//ArrayIndexOutOfBoundsException: 3
    List<Integer> list = List.of(1, 2, 3);
    System.out.println(list.get(3));
            //IndexOutOfBoundsException: Index 3 out-of-bounds for length 3
}catch (Exception e){
    System.out.println(e);
}*/

//运行时异常被抛出可以不处理。即不捕获也不声明抛出。
//默认给虚拟机处理,终止程序,什么时候不抛出运行时异常了,在来继续执行程序
int[] arr = {1,2,3};
System.out.println(arr[3]);//ArrayIndexOutOfBoundsException: 3
List<Integer> list = List.of(1, 2, 3);
System.out.println(list.get(3));
            //IndexOutOfBoundsException: Index 3 out-of-bounds for length 3
System.out.println("后续代码!");

子父类方法中的异常:

​ - 如果父类抛出了多个异常,子类重写父类方法时,抛出和父类相同的异常或者是父类异常的子类或者不抛出异常。

​ - 父类方法没有抛出异常,子类重写父类该方法时也不可抛出异常。此时子类产生该异常,只能捕获处理,不能声明抛出

​ 注意: 父类异常是什么样,子类异常就什么样

自定义异常

1.什么是自定义异常类:

在开发中根据自己业务的异常情况来定义异常类.

自定义一个业务逻辑异常: RegisterException。一个注册异常类。

2)异常类如何定义:

自定义一个编译期异常: 自定义类 并继承于 java.lang.Exception

自定义一个运行时期的异常类:自定义类 并继承于 java.lang.RuntimeException

// 业务逻辑异常
public class RegisterException extends Exception {    
 /*       空参构造      */  
    public RegisterException() {}   
 /* 
     * @param message 表示异常提示   
 */    
  public RegisterException(String message) {   
        super(message);   
  }
}

Lambda表达式

1.函数式编程思想:只要能获取到结果,谁去做的,怎么做的都不重要,重视的是结果,不重视过程

2.格式:

(参数类型 参数名称) ‐> { 一些重写方法的代码 }

() : 接口中抽象方法的参数列表,没有参数,就空着。

-> : 把参数传递给方法

{ }: 重写接口抽象方法的方法体

可以省略的内容:

1.(参数列表):括号中参数列表的数据类型,可以省略不写

2.(参数列表):括号中的参数如果只有一个,那么类型和()都可以省略

3.{一些代码}:如果{}中的代码只有一行,无论是否有返回值,都可以省略({},return,分号)

​ 注意:要省略{},return,分号必须一起省略

使用前提:

  1. 使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法。 无论是JDK内置的 Runnable 、 Comparator 接口还是自定义的接口,只有当接口中的抽象方法存在且唯一 时,才可以使用Lambda。

  2. 使用Lambda必须具有上下文推断。 也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。

备注:有且仅有一个抽象方法的接口,称为“函数式接口”。

3.案例1:cook小例子

/*
    定一个厨子Cook接口,内含唯一的抽象方法makeFood
 */
public interface Cook {
    //定义无参数无返回值的方法makeFood
    public abstract void makeFood();
}


/*
    需求:
        给定一个厨子Cook接口,内含唯一的抽象方法makeFood,且无参数、无返回值。
        使用Lambda的标准格式调用invokeCook方法,打印输出“吃饭啦!”字样
 */
public class Demo01Cook {
    public static void main(String[] args) {
        //调用invokeCook方法,参数是Cook接口,传递Cook接口的匿名内部类对象
        invokeCook(new Cook() {
            @Override
            public void makeFood() {
                System.out.println("吃饭了");
            }
        });

        //使用Lambda表达式,简化匿名内部类的书写
        invokeCook(()->{
            System.out.println("吃饭了");
        });

        //优化省略Lambda
        invokeCook(()-> System.out.println("吃饭了"));
    }

    //定义一个方法,参数传递Cook接口,方法内部调用Cook接口中的方法makeFood
    public static void invokeCook(Cook cook){
        cook.makeFood();
    }
}

4.案例2:使用数组存储多个Person对象

对数组中的Person对象使用Arrays的sort方法通过年龄进行升序排序

//使用数组存储多个Person对象
Person[] arr = {
    new Person("柳岩",38),
    new Person("迪丽热巴",18),
    new Person("古力娜扎",19)
};

//对数组中的Person对象使用Arrays的sort方法通过年龄进行升序(前边-后边)排序
/*Arrays.sort(arr, new Comparator<Person>() {
    @Override
    public int compare(Person o1, Person o2) {
        return o1.getAge()-o2.getAge();
    }
});*/

//使用Lambda表达式,简化匿名内部类
Arrays.sort(arr,(Person o1, Person o2)->{
    return o1.getAge()-o2.getAge();
});

//优化省略Lambda
Arrays.sort(arr,(o1, o2)->o1.getAge()-o2.getAge());

//遍历数组
for (Person p : arr) {
    System.out.println(p);
}

5.案例3: 给定一个计算器Calculator接口,内含抽象方法calc可以将两个int数字相加得到和值

​ 使用Lambda的标准格式调用invokeCalc方法,完成120和130的相加计算

public static void main(String[] args) {
    //调用invokeCalc方法,方法的参数是一个接口,可以使用匿名内部类
    invokeCalc(10, 20, new Calculator() {
        @Override
        public int calc(int a, int b) {
            return a+b;
        }
    });

    //使用Lambda表达式简化匿名内部类的书写
    invokeCalc(120,130,(int a,int b)->{
        return a + b;
    });

    //优化省略Lambda
    invokeCalc(120,130,(a,b)-> a + b);
}

/*
    定义一个方法
    参数传递两个int类型的整数
    参数传递Calculator接口
    方法内部调用Calculator中的方法calc计算两个整数的和
 */
public static void invokeCalc(int a,int b,Calculator c){
    int sum = c.calc(a,b);
    System.out.println(sum);
}

递归

方法自己调用自己

- 递归的分类:

​ - 递归分为两种,直接递归和间接递归。

​ - 直接递归称为方法自身调用自己。

​ - 间接递归可以A方法调用B方法,B方法调用C方法,C方法调用A方法。

- 注意事项:

​ - 递归一定要有条件限定,保证递归能够停止下来,否则会发生栈内存溢出。

​ - 在递归中虽然有限定条件,但是递归次数不能太多。否则也会发生栈内存溢出。

​ - 构造方法,禁止递归

​ 递归的使用前提:当调用方法的时候,方法的主体不变,每次调用方法的参数不同,可以使用递归

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n6K1WyL7-1603932138206)(/clipboard-1602644630640.png)]

累加操作:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gGgtnvoM-1603932138207)(/clipboard-1602644681348.png)]

递归遍历文件目录:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Xpg9fBQX-1603932138207)(/clipboard-1602644690415.png)]

集合篇

Collection集合

1.java.util.Collection接口

2.集合是java中提供的一种容器,可以用来存储多个数据。

3.集合与数组的区别:

* 数组的长度是固定的。集合的长度是可变的。

* 数组中存储的是同一类型的元素,可以存储基本数据类型值。集合存储的都是对象。而且对象的类型可以不一致。在开发中一般当对象多的时候,使用集合进行存储。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0znim4vT-1603932138208)(/clipboard-1602647994856.png)]

4.常用的共性方法:

* public boolean add(E e): 把给定的对象添加到当前集合中 。

* public void clear() :清空集合中所有的元素。

* public boolean remove(E e): 把给定的对象在当前集合中删除。

* public boolean contains(E e): 判断当前集合中是否包含给定的对象。

* public boolean isEmpty(): 判断当前集合是否为空。

* public int size(): 返回集合中元素的个数。

* public Object[] toArray(): 把集合中的元素,存储到数组中。

//创建集合对象,可以使用多态
Collection<String> coll = new ArrayList<>();
System.out.println("coll = " + coll);//[] --不为空,说明重写了toSting方法
boolean b1 = coll.add("张三");//添加元素,返回boolean
coll.add("李四");
coll.add("赵六");
coll.add("田七");
System.out.println("b1 = " + b1);//true
System.out.println(coll);//[张三, 李四, 赵六, 田七]
boolean b2 = coll.remove("赵六");//删除元素,返回boolean
System.out.println("b2 = " + b2);//true
boolean b3 = coll.remove("赵四");
System.out.println("b3 = " + b3);//false
System.out.println(coll);//[张三, 李四, 田七]
boolean b4 = coll.contains("李四");//判断是否包含该对象,返回boolean
System.out.println("b4 = " + b4);//true
boolean b5 = coll.contains("赵四");
System.out.println("b5 = " + b5);//false
boolean b6 = coll.isEmpty();//判断是不是空,返回boolean
System.out.println("b6 = " + b6);//false
int size = coll.size();//得到集合长度
System.out.println("size = " + size);//3
Object[] arr = coll.toArray();//将集合转换为数组,用Object可以接收任意类型的
for (int i = 0; i < arr.length; i++) {
    System.out.println(arr[i]);//张三李四田七
}
coll.clear();//清空集合
System.out.println(coll);//[]

Iterator迭代器

1.java.util.Iterator

2.想要遍历Collection集合,那么就要获取该集合迭代器完成迭代操作,获取迭代器实现类的方法:

public Iterator iterator() : 获取集合对应的迭代器,用来遍历集合中的元素的。

迭代器也是有泛型的,且它的泛型跟着集合走

3.常用方法:

* public E next():返回迭代的下一个元素。

* public boolean hasNext():如果仍有元素可以迭代即如果有下一个元素,则返回 true。

4.使用步骤:

1)使用集合中的方法iterator()获取迭代器的实现类对象。使用Iterator接口接收(多态)

2)使用Iterator接口中的方法hasNext判断还有没有下一个元素

3)使用Iterator接口中的方法next取出集合中的下一个元素

Collection<String> coll = new ArrayList<>();
coll.add("张三");
coll.add("李四");
coll.add("王五");
coll.add("赵六");
coll.add("田七");
Iterator<String> iterator = coll.iterator();
while(iterator.hasNext()){
    System.out.println(iterator.next());
}
//张三李四王五赵六田七
for (Iterator<String> iterator2 = coll.iterator();iterator2.hasNext();) {
    System.out.println(iterator2.next());
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vim4m0Q2-1603932138208)(/clipboard-1602648452267.png)]

5.增强for循环:

1)专门用来遍历数组和集合的。它的内部原理其实是个Iterator迭代器,所以在遍历的过程中,不能对集合中的元素进行增删。

2)格式:

for(元素的数据类型 变量 : Collection集合or数组){

//写操作代码

}

int[] arr = {3,5,6,87};
//使用增强for遍历数组
for(int a : arr){//a代表数组中的每个元素
	System.out.println(a);
}
Collection<String> coll = new ArrayList<String>();
coll.add("小河神");
coll.add("老河神");
coll.add("神婆");
//使用增强for遍历
for(String s :coll){//接收变量s代表 代表被遍历到的集合元素
	System.out.println(s);
}

泛型

1.概念:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w2R7M2DI-1603932138208)(/clipboard-1602648657351.png)]

2.创建集合对象,不使用泛型

好处:默认的是Object类型,什么都可以存储

坏处:不安全,会发生异常

ArrayList list = new ArrayList();
list.add("abc");
list.add(1);
//使用迭代器遍历list集合
//获取迭代器
Iterator it = list.iterator();
//使用迭代器中的方法hasNext和next遍历集合
while(it. hasNext()){
    //取出元素也是object类型
    Object obj = it.next();
    System.out.println(obj);
    //想要使用String类特有的方法, length获取字符串的长度;不能使用多态 object obj = "abc";
    //需要向下转型
    //会抛出CLassCastException类型转换异常,不能把Integer类型转换为String类型
    String S = (String)obj;
    System. out . println(s.length());
}

3.创建集合对象,使用泛型

好处:避免类型转换的麻烦,把运行异常提升到了编译器

坏处:规定死了类型

4.定义一个含有泛型的类

格式:修饰符 class 类名<代表泛型的变量> { //… }

public class GenericClass<E> {
    private E name;
	public E getName() {
		return name;
    }
	public void setName(E name) {
		this.name = name;
    }
 }

5.定义含有泛型的方法:

格式:修饰符 <泛型> 返回值类型 方法名(参数){}

public <M> void method01(M m){
    sout(m);
}
method01(10);//10
method01("hh");//hh
method01(10.23);//10.23

6.定义含有泛型的接口

/*
    含有泛型的接口,第一种使用方式:定义接口的实现类,实现接口,指定接口的泛型,
    public interface Iterator<E> {
    	E next();
   	}
    Scanner类实现了Iterator接口,并指定接口的泛型为String,所以重写的next方法泛型默认就是String
    public final class Scanner implements Iterator<String>{
  	  public String next() {}
    }
*/
public class GenericInterfaceImpl1 implements GenericInterface<String>{
    @Override
    public void method(String s) {
    	System. out . println(s);
    }
}

/*
    含有泛型的接口第二种使用方式:接口使用什么泛型,实现类就使用什么泛型,类跟着接口走就相当于定义了一个含有泛型的类,创建对象的时候确定泛型的类型
    public interface List<E>{
    	boolean add(E e);
    	E get(int index);
    }
    public class ArrayList<E> implements List<E>{
   		public boolean add(E e) {}
    	public E get(int index) {}
    }
*/
public class GenericInterfaceImpl2<I> implements GenericInterface<I> {
    @Override
    public void method(I i) {
    	System.out.println(i);
    }
}

/*
测试含有泛型的接口
*/
public class Demo04GenericInterface {
	public static void main(String[] args) {
	//创建GenericInterfaceImpl1对象
	GenericInterfaceImpl1 gi1 = new GenericInterfaceImpl2();
	gi1.method("字符串");
	//创建GenericInterfaceImpl2对象
	GenericInterfaceImpl2<Integer> gi2 = new GenericInterfaceImpl2<>();
	gi2.method(10);
	GenericInterfaceImpl2<Double> gi3 = new GenericInterfaceImpl2<>();
	gi3.method(8.8);
	}
}

7.泛型的通配符

?:代表任意数据类型

使用方式:不能创建对象使用,只能作为方法的参数使用

public static void main(String[] args) {
    Collection<Integer> list1 = new ArrayList<Integer>();
    getElement(list1);
    Collection<String> list2 = new ArrayList<String>();
    getElement(list2);
}
public static void getElement(Collection<?> coll){
    coll.itrator();//用迭代器遍历时,it.next()的返回值是Object类型的
}
//?代表可以接收任意类型
tips:泛型不存在继承关系
//Collection<Object> list = new ArrayList<String>();这种是错误的。

8.受限泛型:

泛型的上限限定:? extends E 代表使用的泛型只能是E类型的子类/本身

泛型的下限限定:? super E 代表使用的泛型只能是E类型的父类/本身

比如:现已知0bject类, String类, Number类, Integer类,其中Number是Integer的父类
public static void main(String[] args) {
    Collection<Integer> list1 = new ArrayList<Integer>();
    collection<String> list2 = new ArrayList<String>();
    Collection<Number> list3 = new Arraylist<Number>(); 
    Collection<object> list4 = new ArrayList<object>();
    getElement(list1);
    getElement(list2);//报错
    getElement(list3);
    getElement(list4);//报错

    getElement2(list1);//报错
    getElement2(list2);//报错
    getElement2(list3);
    getElement2(list4);
}
//泛型的上限:此时的泛型?,必须是Number类型或者Number类型的子类
public static void getElement1(Collection<? extends Number> coll){}
//泛型的下限:此时的泛型?,必须是Number类型或者Number类型的父类
public static void getElement2(Collection<? super Number> coll){}

List集合

1.java.util.List

2.三大特点:有序的集合,存储顺序与取出顺序一致

有索引,包含了一些带索引的方法

允许存储重复元素

3.常用的带索引的方法:

public void add(int index, E element) : 将指定的元素,添加到该集合中的指定位置上。

public E get(int index) :返回集合中指定位置的元素。

public E remove(int index) : 移除列表中指定位置的元素, 返回的是被移除的元素。

public E set(int index, E element) :用指定元素替换集合中指定位置的元素,返回值的更新前的元素。

注意:要注意指针越界

​ IndexOutOfBoundsException:索引越界异常,集合会报

​ ArrayIndexOutOfBoundsException:数组索引越界异常

​ StringIndexOutOfBoundsException:字符串索引越界异常

//创建一个List集合对象,多态List<String> list = new ArrayList<>();
//使用add方法往集合中添加元素
list.add("a");list.add("b");
list.add("c");
list.add("d");
list.add("a");
//打印集合
System.out.println(list);//[a, b, c, d, a]  不是地址重写了toString

//public void add(int index, E element): 将指定的元素,
//添加到该集合中的指定位置上。
//在c和d之间添加一个itheima
list.add(3,"itheima");//[a, b, c, itheima, d, a]
System.out.println(list);

//public E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素。
//移除元素
String removeE = list.remove(2);
System.out.println("被移除的元素:"+removeE);//被移除的元素:c
System.out.println(list);//[a, b, itheima, d, a]

//public E set(int index, E element):用指定元素替换集合中指定位置的元素,
//返回值的更新前的元素。
//把最后一个a,替换为A
String setE = list.set(4, "A");
System.out.println("被替换的元素:"+setE);//被替换的元素:a
System.out.println(list);//[a, b, itheima, d, A]

//List集合遍历有3种方式
//使用普通的for循环
for(int i=0; i<list.size(); i++){
     //public E get(int index):返回集合中指定位置的元素。
     String s = list.get(i);
     System.out.println(s);
}
System.out.println("-----------------");
//使用迭代器
Iterator<String> it = list.iterator();
while(it.hasNext()){
    String s = it.next();
    System.out.println(s);
}
System.out.println("-----------------");
//使用增强for
for (String s : list) {
   System.out.println(s);
}

String r = list.get(5);//IndexOutOfBoundsException: 
//Index 5 out-of-bounds for length 5
System.out.println(r);
ArrayList集合

1.增删慢,查找快。
2.对于ArrayList来说,有一个代表泛型。
泛型:规定装在该集合之中的元素类型
注意:泛型只能是引用类型,不能是基本类型,但可以放基本类型的包装类
包装类:byte-Byte,short-Short,int-Integer,long-Long,float-Float,double-Double,char-Character,boolean-Boolean
3.常用方法:
public boolean add(E e)—添加元素,对于arraylist添加一定会成功,而其他集合就不一定了
public E get(int index)—取出元素,索引值从0开始
public E remove(int index)—删除元素
public int size()—获取元素个数

ArrayList<String> list = new Arraylist<>();
//直接打印对象名,出来的是内容,而不是地址,若内容为空。打印:[]
System.out.println(list);//[]

list.add("aaa"); //向集合中添加元素public boolean add(E e)
list.add("bbb");
System.out.println(list); //[aaa,bbb]

String name = list.get(1);//获得bbb字符串

ArrayList<Integer> list2 = new Arraylist<>();
list2.add(100);
int a = list2.get(0); //可以自动拆箱、自动装箱,直接写
LinkedList集合

1.增删快,查找慢

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ls4YSQkh-1603932138209)(/clipboard-1602659560484.png)]

2.大量包含收尾操作的方法:
public void addFirst(E e) :将指定元素插入此列表的开头。
public void push(E e) :将元素推入此列表所表示的堆栈。----等效于addFirst()
public void addLast(E e) :将指定元素添加到此列表的结尾。 -----等效于add()

//创建LinkedList集合对象
LinkedList<String> linked = new LinkedList<>();
//使用add方法往集合中添加元素
linked.add("a");
linked.add("b");
linked.add("c");
System.out.println(linked);//[a, b, c]
//linked.addFirst("www");or
linked.push("www");
System.out.println(linked);//[www, a, b, c]
linked.addLast("com");
System.out.println(linked);//[www, a, b, c, com]

public E getFirst() :返回此列表的第一个元素。

public E getLast() :返回此列表的后一个元素。

public boolean isEmpty() :如果列表不包含元素,则返回true。

//创建LinkedList集合对象
LinkedList<String> linked = new LinkedList<>();
//使用add方法往集合中添加元素
linked.add("a");
linked.add("b");
linked.add("c");

//linked.clear();//清空集合中的元素 在获取集合中的元素会
//抛出NoSuchElementException

//public boolean isEmpty():如果列表不包含元素,则返回true。
if(!linked.isEmpty()){
    String first = linked.getFirst();
    System.out.println(first);//a
    String last = linked.getLast();
    System.out.println(last);//c
}

public E removeFirst() :移除并返回此列表的第一个元素。
public E removeLast() :移除并返回此列表的后一个元素。
public E pop() :从此列表所表示的堆栈处弹出一个元素。 ----等效于removeFirst

//创建LinkedList集合对象
LinkedList<String> linked = new LinkedList<>();
//使用add方法往集合中添加元素
linked.add("a");
linked.add("b");
linked.add("c");
System.out.println(linked);//[a, b, c]

//String first = linked.removeFirst();
String first = linked.pop();
System.out.println("被移除的第一个元素:"+first);//a
String last = linked.removeLast();
System.out.println("被移除的最后一个元素:"+last);//c
System.out.println(linked);//[b]

Set集合

1.特点:

不允许存储重复的元素

没有索引,没有带索引的方法,也不能使用普通的for循环遍历

HashSet集合

1.特点:

是一个无序的集合,存储元素和取出元素的顺序有可能不一致

底层是一个哈希表结构(查询的速度非常的快)

Set<Integer> set = new HashSet<>();
//使用add方法往集合中添加元素
set.add(1);
set.add(3);
set.add(2);
set.add(1);
//使用迭代器遍历set集合
Iterator<Integer> it = set.iterator();
while (it.hasNext()){
     Integer n = it.next();
     System.out.println(n);//1,2,3
}
//使用增强for遍历set集合
System.out.println("-----------------");
for (Integer i : set) {
     System.out.println(i);
}

2.哈希值:是一个十进制的整数,由系统随机给出(就是对象的地址值,是一个逻辑地址,是模拟出来得到地址,不是数据实际存储的物理地址)
在Object类有一个方法,可以获取对象的哈希值
int hashCode() 返回该对象的哈希码值。
hashCode方法的源码:
public native int hashCode();
native:代表该方法调用的是本地操作系统的方法

//Person类继承了Object类,所以可以使用Object类的hashCode方法
Person p1 = new Person();
int h1 = p1.hashCode();
System.out.println(h1);//1967205423  | 1

Person p2 = new Person();
int h2 = p2.hashCode();
System.out.println(h2);//42121758   |  1

/*
    toString方法的源码:
      return getClass().getName() + "@" + Integer.toHexString(hashCode());
*/
System.out.println(p1);//com.itheima.demo03.hashCode.Person@75412c2f
System.out.println(p2);//com.itheima.demo03.hashCode.Person@282ba1e
System.out.println(p1==p2);//false

3.String类的hashCode方法

//首先来看一下String中hashCode方法的实现源码
public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        char val[] = value;
        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];
        }
        hash = h;
    }
    return h;
}

在String类中有个私有实例字段hash表示该串的哈希值,在第一次调用hashCode方法时,字符串的哈希值被计算并且赋值给hash字段,之后再调用hashCode方法便可以直接取hash字段返回。

String类中的hashCode计算方法还是比较简单的,就是以31为权,每一位为字符的ASCII值进行运算,用自然溢出来等效取模。

字符串哈希可以做很多事情,通常是类似于字符串判等,判回文之类的。

但是仅仅依赖于哈希值来判断其实是不严谨的,除非能够保证不会有哈希冲突,通常这一点很难做到。就拿jdk中String类的哈希方法来举例,字符串"gdejicbegh"与字符串"hgebcijedg"具有相同的hashCode()返回值-801038016,并且它们具有reverse的关系。这个例子说明了用jdk中默认的hashCode方法判断字符串相等或者字符串回文,都存在反例。

/*
     String类的哈希值
        String类重写Obejct类的hashCode方法
*/
String s1 = new String("abc");
String s2 = new String("abc");
System.out.println(s1.hashCode());//96354
System.out.println(s2.hashCode());//96354

System.out.println("重地".hashCode());//1179395
System.out.println("通话".hashCode());//1179395

4.哈希表的结构:数组+链表+红黑树

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r5pr8SJr-1603932138209)(/clipboard-1602672763749.png)]

4.不存储重复元素的原理分析:
前提:存储的元素必须重写hashCode和equals方法!!!!
String类,已经重写过了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qZpHAqYe-1603932138210)(/clipboard-1602673355997.png)]

LinkedHashSet集合

1.特点:底层是哈希表(数组+链表/红黑树)+ 链表,多了一个记录元素顺序的链表

HashSet<String> set = new HashSet<>();
set.add("www");
set.add("abc");
set.add("abc");
set.add("itcast");
System.out.println(set);//[abc, www, itcast] 无序,不允许重复

LinkedHashSet<String> linked = new LinkedHashSet<>();
linked.add("www");
linked.add("abc");
linked.add("abc");
linked.add("itcast");
System.out.println(linked);//[www, abc, itcast] 有序,不允许重复

Collections工具类

1.java.utils.Collections 是集合工具类,用来对集合进行操作。
2.常用方法:
public static boolean addAll(Collection c, T… elements) :往集合中添加一些元素。

public static void shuffle(List<?> list) 打乱顺序 :打乱集合顺序。

ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"a","b","c","d","e");
System.out.println(list);//[a, b, c, d, e]
Collections.shuffle(list);
System.out.println(list);//[b, d, c, a, e]

public static void sort(List list) :将集合中元素按照默认规则排序。
sort方法的使用前提:
被排序的集合里面的元素,必须实现Comparable接口,重写其中的conpareTo定义排序的规则
Comparable接口的排序规则:自己(this)-参数:升序

ArrayList<Integer> list01 = new ArrayList<>();
list01.add(1);
list01.add(3);
list01.add(2);
System.out.println(list01);//[1, 3, 2]

//public static <T> void sort(List<T> list):将集合中元素按照默认规则排序。
Collections.sort(list01);//默认是升序
System.out.println(list01);//[1, 2, 3]

ArrayList<String> list02 = new ArrayList<>();
list02.add("a");
list02.add("c");
list02.add("b");
System.out.println(list02);//[a, c, b]
Collections.sort(list02);
System.out.println(list02);//[a, b, c]

ArrayList<Person> list03 = new ArrayList<>();
list03.add(new Person("张三",18));
list03.add(new Person("李四",20));
list03.add(new Person("王五",15));
System.out.println(list03);
//[Person{name='张三', age=18}, Person{name='李四', age=20},
// Person{name='王五', age=15}]
/*   person类中的方法:
    //重写排序的规则
    @Override
    public int compareTo(Person o) {
        //return 0;//认为元素都是相同的
        //自定义比较的规则,比较两个人的年龄(this,参数Person)
        //return this.getAge() - o.getAge();//年龄升序排序
        return o.getAge() - this.getAge();//年龄降序排序
    }
*/  
Collections.sort(list03);
System.out.println(list03);// [Person{name='李四', age=20},
//Person{name='张三', age=18},Person{name='王五', age=15}]

public static void sort(List list,Comparator<? super T> ) :将集合中元素按照指定规则排序
注意:
Comparator和Comparable的区别
Comparable:自己(this)和别人(参数)比较,自己需要实现Comparable接口,重写比较的规则compareTo方法
Comparator:相当于找一个第三方的裁判,比较两个

ArrayList<Integer> list01 = new ArrayList<>();
list01.add(1);
list01.add(3);
list01.add(2);
System.out.println(list01);//[1, 3, 2]

Collections.sort(list01, new Comparator<Integer>() {
    //重写比较的规则
    @Override
   public int compare(Integer o1, Integer o2) {
       //return o1-o2;//升序
       return o2-o1;//降序
   }
});
System.out.println(list01);

ArrayList<Student> list02 = new ArrayList<>();
list02.add(new Student("a迪丽热巴",18));
list02.add(new Student("古力娜扎",20));
list02.add(new Student("杨幂",17));
list02.add(new Student("b杨幂",18));
System.out.println(list02);

/*Collections.sort(list02, new Comparator<Student>() {
    @Override
    public int compare(Student o1, Student o2) {
         //按照年龄升序排序
         return o1.getAge()-o2.getAge();
    }
});*/

//扩展:了解
Collections.sort(list02, new Comparator<Student>() {
      @Override
      public int compare(Student o1, Student o2) {
          //按照年龄升序排序
          int result =  o1.getAge()-o2.getAge();
          //如果两个人年龄相同,再使用姓名的第一个字比较
          if(result==0){
               result =  o1.getName().charAt(0)-o2.getName().charAt(0);
          }
          return  result;
      }
});
System.out.println(list02);

Map<K,V>集合

1.特点:
1) Map集合是一个双列集合,一个元素包含两个值(一个key,一个value)
2) Map集合中的元素,key和value的数据类型可以相同,也可以不同
3) Map集合中的元素,key是不允许重复的,value是可以重复的
4) Map集合中的元素,key和value是一一对应
2.java.util.HashMap<k,v>集合 implements Map<k,v>接口
HashMap集合的特点:
1.HashMap集合底层是哈希表:查询的速度特别的快
JDK1.8之前:数组+单向链表
JDK1.8之后:数组+单向链表|红黑树(链表的长度超过8):提高查询的速度
2.hashMap集合是一个无序的集合,存储元素和取出元素的顺序有可能不一致
3.但是,key是有序的,会自动排序 1、2、3
3.java.util.LinkedHashMap<k,v>集合 extends HashMap<k,v>集合
LinkedHashMap的特点:
1.LinkedHashMap集合底层是哈希表+链表(保证迭代的顺序)
2.LinkedHashMap集合是一个有序的集合,存储元素和取出元素的顺序是一致的
4.常用方法:
public V put(K key, V value): 把指定的键与指定的值添加到Map集合中。
返回值:v
存储键值对的时候,key不重复,返回值V是null
存储键值对的时候,key重复,会使用新的value替换map中重复的value,返回被替换的value值

//创建Map集合对象,多态
Map<String,String> map = new HashMap<>();
String v1 = map.put("李晨", "范冰冰1");
System.out.println("v1:"+v1);//v1:null
String v2 = map.put("李晨", "范冰冰2");
System.out.println("v2:"+v2);//v2:范冰冰1
System.out.println(map);//{李晨=范冰冰2}

map.put("冷锋","龙小云");
map.put("杨过","小龙女");
map.put("尹志平","小龙女");
System.out.println(map);
//{杨过=小龙女, 尹志平=小龙女, 李晨=范冰冰2, 冷锋=龙小云}

public V remove(Object key): 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。
返回值:V
key存在,v返回被删除的值
key不存在,v返回null

//创建Map集合对象
Map<String,Integer> map = new HashMap<>();
map.put("赵丽颖",168);
map.put("杨颖",165);
map.put("林志玲",178);
System.out.println(map);//{林志玲=178, 赵丽颖=168, 杨颖=165}

Integer v1 = map.remove("林志玲");
System.out.println("v1:"+v1);//v1:178
System.out.println(map);//{赵丽颖=168, 杨颖=165}

//int v2 = map.remove("林志颖");//自动拆箱
//是可以使用Int,自动拆箱,但是如果返回为null时,不能返回给int ,得返回给integer,返回给Int会报异常NullPointerException
Integer v2 = map.remove("林志颖");
System.out.println("v2:"+v2);//v2:null
System.out.println(map);//{赵丽颖=168, 杨颖=165}

public V get(Object key) 根据指定的键,在Map集合中获取对应的值。
返回值:V
key存在,返回对应的value值
key不存在,返回null

//创建Map集合对象
Map<String,Integer> map = new HashMap<>();
map.put("赵丽颖",168);
map.put("杨颖",165);
map.put("林志玲",178);
Integer v1 = map.get("杨颖");
System.out.println("v1:"+v1);//v1:165
Integer v2 = map.get("迪丽热巴");
System.out.println("v2:"+v2);//v2:null

boolean containsKey(Object key) 判断集合中是否包含指定的键。
包含返回true,不包含返回false

//创建Map集合对象
Map<String,Integer> map = new HashMap<>();
map.put("赵丽颖",168);
map.put("杨颖",165);
map.put("林志玲",178);
boolean b1 = map.containsKey("赵丽颖");
System.out.println("b1:"+b1);//b1:true
boolean b2 = map.containsKey("赵颖");
System.out.println("b2:"+b2);//b2:false

5.Map集合的遍历方式:
Map集合的第一种遍历方式:通过键找值的方式
Map集合中的方法:
Set keySet() 返回此映射中包含的键的 Set 视图。
实现步骤:
1.使用Map集合中的方法keySet(),把Map集合所有的key取出来,存储到一个Set集合中
2.遍历set集合,获取Map集合中的每一个key
3.通过Map集合中的方法get(key),通过key找到value

//创建Map集合对象
Map<String,Integer> map = new HashMap<>();
map.put("赵丽颖",168);
map.put("杨颖",165);
map.put("林志玲",178);
//1.使用Map集合中的方法keySet(),把Map集合所有的key取出来,存储到一个Set集合中
Set<String> set = map.keySet();
//2.遍历set集合,获取Map集合中的每一个key
//使用迭代器遍历Set集合
Iterator<String> it = set.iterator();
while (it.hasNext()){
     String key = it.next();
     //3.通过Map集合中的方法get(key),通过key找到value
     Integer value = map.get(key);
     System.out.println(key+"="+value);
}
System.out.println("-------------------");
//使用增强for遍历Set集合
for(String key : set){
     //3.通过Map集合中的方法get(key),通过key找到value
     Integer value = map.get(key);
     System.out.println(key+"="+value);
}
System.out.println("-------------------");
//使用增强for遍历Set集合
for(String key : map.keySet()){
     //3.通过Map集合中的方法get(key),通过key找到value
     Integer value = map.get(key);
     System.out.println(key+"="+value);
}

Map集合遍历的第二种方式:使用Entry对象遍历
Map集合中的方法:
Set<Map.Entry<K,V>> entrySet() 返回此映射中包含的映射关系的 Set 视图。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Szq2xme0-1603932138210)(/clipboard-1602675387116.png)]

实现步骤:
1.使用Map集合中的方法entrySet(),把Map集合中多个Entry对象取出来,存储到一个Set集合中
2.遍历Set集合,获取每一个Entry对象
3.使用Entry对象中的方法getKey()和getValue()获取键与值

//创建Map集合对象
Map<String,Integer> map = new HashMap<>();
map.put("赵丽颖",168);
map.put("杨颖",165);
map.put("林志玲",178);

//1.使用Map集合中的方法entrySet(),把Map集合中多个Entry对象取出来,存储到一个Set集合中
Set<Map.Entry<String, Integer>> set = map.entrySet();

//2.遍历Set集合,获取每一个Entry对象
//使用迭代器遍历Set集合
Iterator<Map.Entry<String, Integer>> it = set.iterator();
while(it.hasNext()){
    Map.Entry<String, Integer> entry = it.next();
    //3.使用Entry对象中的方法getKey()和getValue()获取键与值
    String key = entry.getKey();
    Integer value = entry.getValue();
    System.out.println(key+"="+value);
}
System.out.println("-----------------------");
for(Map.Entry<String,Integer> entry:set){
    //3.使用Entry对象中的方法getKey()和getValue()获取键与值
    String key = entry.getKey();
    Integer value = entry.getValue();
    System.out.println(key+"="+value);
}
HashMap集合

1.Map集合保证key是唯一的:作为key的元素,必须重写hashCode方法和equals方法,以保证key唯一

/*
    HashMap存储自定义类型键值
      key:Person类型
         Person类就必须重写hashCode方法和equals方法,以保证key唯一
    value:String类型
         可以重复
*/
//创建HashMap集合
HashMap<Person,String> map = new HashMap<>();
//往集合中添加元素
map.put(new Person("女王",18),"英国");
map.put(new Person("秦始皇",18),"秦国");
map.put(new Person("普京",30),"俄罗斯");
map.put(new Person("女王",18),"毛里求斯");
//使用entrySet和增强for遍历Map集合
Set<Map.Entry<Person, String>> set = map.entrySet();
for (Map.Entry<Person, String> entry : set) {
     Person key = entry.getKey();
     String value = entry.getValue();
     System.out.println(key+"-->"+value);
}
LinkedHashMap集合

1.java.util.LinkedHashMap<K,V> entends HashMap<K,V>
2.Map 接口的哈希表和链接列表实现,具有可预知的迭代顺序。
底层原理:哈希表+链表(记录元素的顺序)

LinkedHashMap<String,String> linked = new LinkedHashMap<>();
linked.put("a","a");
linked.put("c","c");
linked.put("b","b");
linked.put("a","d");
System.out.println(linked);// key不允许重复,有序 {a=d, c=c, b=b}
Hashtable集合

1.java.util.Hashtable<K,V>集合 implements Map<K,V>接口

2.Hashtable:底层也是一个哈希表,是一个线程安全的集合,是单线程集合,速度慢

HashMap:底层是一个哈希表,是一个线程不安全的集合,是多线程的集合,速度快。

3.HashMap集合(之前学的所有的集合):可以存储null值,null键

Hashtable集合不能存储null值,null键

4.Hashtable和Vector集合一样,在jdk1.2版本之后被更先进的集合(HashMap,ArrayList)取代了Hashtable的子类 。

Properties依然活跃在历史舞台

Properties集合是一个唯一和IO流相结合的集合。

HashMap<String,String> map = new HashMap<>();
map.put(null,"a");
map.put("b",null);
map.put(null,null);
System.out.println(map);//{null=null, b=null}

Hashtable<String,String> table = new Hashtable<>();
//table.put(null,"a");//NullPointerException
//table.put("b",null);//NullPointerException
table.put(null,null);//NullPointerException

JDK9新特性

List接口,Set接口,Map接口:里边增加了一个静态的方法of,可以给集合一次性添加多个元素
static List of(E… elements)
使用前提:
当集合中存储的元素的个数已经确定了,不在改变时使用
注意:
1.of方法只适用于List接口,Set接口,Map接口,不适用于接口的实现类
2.of方法的返回值是一个不能改变的集合,集合不能再使用add,put方法添加元素,会抛出异常
3.Set接口和Map接口在调用of方法的时候,不能有重复的元素,否则会抛出异常

List<String> list = List.of("a", "b", "a", "c", "d");
System.out.println(list);//[a, b, a, c, d]
//list.add("w");//UnsupportedOperationException:不支持操作异常

//Set<String> set = Set.of("a", "b", "a", "c", "d");
//IllegalArgumentException:非法参数异常,有重复的元素
Set<String> set = Set.of("a", "b", "c", "d");
System.out.println(set);
//set.add("w");//UnsupportedOperationException:不支持操作异常

//Map<String, Integer> map = Map.of("张三", 18, "李四", 19, "王五", 20,"张三",19);
//IllegalArgumentException:非法参数异常,有重复的元素
Map<String, Integer> map = Map.of("张三", 18, "李四", 19, "王五", 20);
System.out.println(map);//{王五=20, 李四=19, 张三=18}
//map.put("赵四",30);//UnsupportedOperationException:不支持操作异常

IO操作篇

File类

1.java.io.File 类是文件和目录路径名的抽象表示,主要用于文件和目录的创建、查找和删除等操作
2.File类是一个与系统无关的类,任何的操作系统都可以使用这个类中的方法
3.重点:记住这三个单词 file:文件 directory:文件夹/目录 path:路径
4.四个常量:
static String pathSeparator 与系统有关的路径分隔符,为了方便,它被表示为一个字符串。
static char pathSeparatorChar 与系统有关的路径分隔符。
static String separator 与系统有关的默认名称分隔符,为了方便,它被表示为一个字符串。
static char separatorChar 与系统有关的默认名称分隔符。

/*
    操作路径:路径不能写死了
    C:\develop\a\a.txt  windows
    C:/develop/a/a.txt  linux
    "C:"+File.separator+"develop"+File.separator+"a"+File.separator+"a.txt"
 */
String pathSeparator = File.pathSeparator;
System.out.println(pathSeparator);打印:;
//路径分隔符 windows:分号;  linux:冒号:

String separator = File.separator;
System.out.println(separator);打印:\
// 文件名称分隔符 windows:反斜杠\  linux:正斜杠/

5.路径:

绝对路径,是一个完整路径。以盘符(c:.D:)开始的路径。

eg: c:\a.txt 、 C:\Users\itcast\IdeaProjects\shungyuan\123.txt 、 D:\demo\b.txt

相对路径,是一个简化的路径。相对指的是相对于当前项目的根目录(C:\Users\itcast\IdeaProjects\shungyuan)

如果使用当前项目的根目录,路径可以简化书写。C:\Users\itcast\IdeaProjects\shungyuan\123.txt–>简化为: 123.txt(可以省略项目的根目录)

注意:

​ 1.路径是不区分大小写

​ 2.路径中的文件名称分隔符windows使用反斜杠,反斜杠是转义字符,两个反斜杠代表一个普通的反斜杠

构造方法

1)public File(String pathname) :通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例
参数:
String pathname:字符串的路径名称
路径可以是以文件结尾,也可以是以文件夹结尾
路径可以是相对路径,也可以是绝对路径
路径可以是存在,也可以是不存在
创建File对象,只是把字符串路径封装为File对象,不考虑路径的真假情况

File f1 = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan\\a.txt");
System.out.println(f1);
//重写了Object类的toString方法 C:\Users\itcast\IdeaProjects\shungyuan\a.txt

File f2 = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan");
System.out.println(f2);//C:\Users\itcast\IdeaProjects\shungyuan

File f3 = new File("b.txt");
System.out.println(f3);//b.txt

2)public File(String parent, String child) :从父路径名字符串和子路径名字符串创建新的 File实例。
参数 : 把路径分成了两部分 String parent:父路径 String child:子路径
好处 : 父路径和子路径,可以单独书写,使用起来非常灵活;父路径和子路径都可以变化

File file = new File("c:\\","a.txt");
System.out.println(file);//c:\a.txt

3)public File(File parent, String child) :从父抽象路径名和子路径名字符串创建新的 File实例
参数:把路径分成了两部分 File parent:父路径 String child:子路径
好处:
父路径和子路径,可以单独书写,使用起来非常灵活;父路径和子路径都可以变化
父路径是File类型,可以使用File的方法对路径进行一些操作,再使用路径创建对象

File parent = new File("c:\\");
File file = new File(parent,"hello.java");
System.out.println(file);//c:\hello.java
常用方法

public String getAbsolutePath() :返回此File的绝对路径名字符串。
无论传进去的路径是绝对的还是相对的,getAbsolutePath方法返回的都是绝对路径

File f1 = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan\\a.txt");
String absolutePath1 = f1.getAbsolutePath();
System.out.println(absolutePath1);
//C:\Users\itcast\IdeaProjects\shungyuan\a.txt

File f2 = new File("a.txt");
String absolutePath2 = f2.getAbsolutePath();
System.out.println(absolutePath2);
//C:\Users\itcast\IdeaProjects\shungyuan\a.txt

public String getPath() :将此File转换为路径名字符串。
toString方法调用的就是getPath方法
源码:
public String toString() {
return getPath();
}

File f1 = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan\\a.txt");
File f2 = new File("a.txt");
String path1 = f1.getPath();
System.out.println(path1);//C:\Users\itcast\IdeaProjects\shungyuan\a.txt
String path2 = f2.getPath();
System.out.println(path2);//a.txt

System.out.println(f1);//C:\Users\itcast\IdeaProjects\shungyuan\a.txt
System.out.println(f1.toString());//C:\Users\itcast\IdeaProjects\shungyuan\a.txt

public String getName() :返回由此File表示的文件或目录的名称。
获取的就是构造方法传递路径的结尾部分(文件/文件夹)

File f1 = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan\\a.txt");
String name1 = f1.getName();
System.out.println(name1);//a.txt

File f2 = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan");
String name2 = f2.getName();
System.out.println(name2);//shungyuan

public long length() :返回由此File表示的文件的长度。
获取的是构造方法指定的文件的大小,以字节为单位
注意:
文件夹是没有大小概念的,不能获取文件夹的大小,返回的也是0
如果构造方法中给出的路径不存在,那么length方法返回0

File f1 = new File("C:\\develop\\a\\1.jpg");
long l1 = f1.length();
System.out.println(l1);//780831

File f2 = new File("C:\\develop\\a\\2.jpg");
System.out.println(f2.length());//0

File f3 = new File("C:\\develop\\a");
System.out.println(f3.length());//0 文件夹没有大小概念的
判断功能的方法

public boolean exists() :此File表示的文件或目录是否实际存在。
用于判断构造方法中的路径是否存在 存在:true 不存在:false

File f1 = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan");
System.out.println(f1.exists());//true

File f2 = new File("C:\\Users\\itcast\\IdeaProjects\\shung");
System.out.println(f2.exists());//false

File f3 = new File("shungyuan.iml");//根目录是当前项目的根目录
//相对路径 C:\Users\itcast\IdeaProjects\shungyuan\shungyuan.iml
System.out.println(f3.exists());//true

File f4 = new File("a.txt");
System.out.println(f4.exists());//false

public boolean isDirectory() :此File表示的是否为目录。
public boolean isFile() :此File表示的是否为文件
注意:
电脑的硬盘中只有文件/文件夹,两个方法是互斥
这两个方法使用前提,路径必须是存在的,否则都返回false

File f1 = new File("C:\\Users\\itcast\\IdeaProjects\\shung");
//不存在,就没有必要获取
if(f1.exists()){
    System.out.println(f1.isDirectory());
    System.out.println(f1.isFile());
}

File f2 = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan");
if(f2.exists()){
    System.out.println(f2.isDirectory());//true
    System.out.println(f2.isFile());//false
}

File f3 = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan\\shungyuan.iml");
if(f3.exists()){
    System.out.println(f3.isDirectory());//false
    System.out.println(f3.isFile());//true
}
创建删除功能的方法

public boolean createNewFile() :当且仅当具有该名称的文件尚不存在时,创建一个新的空文件。
创建文件的路径和名称在构造方法中给出(构造方法的参数)
返回值:布尔值
true:文件不存在,创建文件,返回true
false:文件存在,不会创建,返回false
注意:
1.此方法只能创建文件,不能创建文件夹
2.创建文件的路径必须存在,否则会抛出异常
public boolean createNewFile() throws IOException
createNewFile声明抛出了IOException,我们调用这个方法,就必须的处理这个异常,要么throws,要么trycatch

private static void show01() throws IOException {
    File f1 = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan\\08_FileAndRecursion\\1.txt");
    boolean b1 = f1.createNewFile();
    System.out.println("b1:"+b1);

    File f2 = new File("08_FileAndRecursion\\2.txt");
    System.out.println(f2.createNewFile());

    File f3 = new File("08_FileAndRecursion\\新建文件夹");
    System.out.println(f3.createNewFile());
    //不要被名称迷糊,要看类型,此处仍然可以创建,并且创建了一个文件

    File f4 = new File("08_FileAndRecursi\\3.txt");
    System.out.println(f4.createNewFile());//路径不存在,抛出IOException
}

public boolean mkdir() :创建由此File表示的目录。
public boolean mkdirs() :创建由此File表示的目录,包括任何必需但不存在的父目录
返回值:布尔值
true:文件夹不存在,创建文件夹,返回true
false:文件夹存在,不会创建,返回false;构造方法中给出的路径不存在返回false
注意:
1.此方法只能创建文件夹,不能创建文件

File f1 = new File("08_FileAndRecursion\\aaa");
boolean b1 = f1.mkdir();
System.out.println("b1:"+b1);

File f2 = new File("08_FileAndRecursion\\111\\222\\333\\444");
boolean b2 = f2.mkdirs();
System.out.println("b2:"+b2);

File f3 = new File("08_FileAndRecursion\\abc.txt");
boolean b3 = f3.mkdirs();//看类型,是一个文件
System.out.println("b3:"+b3);

File f4 = new File("08_F\\ccc");
boolean b4 = f4.mkdirs();//不会抛出异常,路径不存在,不会创建
System.out.println("b4:"+b4);

public boolean delete() :删除由此File表示的文件或目录。
此方法,可以删除构造方法路径中给出的文件/文件夹
返回值:布尔值
true:文件/文件夹删除成功,返回true
false:文件夹中有内容,不会删除返回false;构造方法中路径不存在false
注意:
delete方法是直接在硬盘删除文件/文件夹,不走回收站,删除要谨慎

File f1 = new File("08_FileAndRecursion\\新建文件夹");
boolean b1 = f1.delete();
System.out.println("b1:"+b1);

File f2 = new File("08_FileAndRecursion\\abc.txt");
System.out.println(f2.delete());
目录的遍历

public String[] list() :返回一个String数组,表示该File目录中的所有子文件或目录。
public File[] listFiles() :返回一个File数组,表示该File目录中的所有的子文件或目录
注意:
list方法和listFiles方法遍历的是构造方法中给出的目录
如果构造方法中给出的目录的路径不存在,会抛出空指针异常
如果构造方法中给出的路径不是一个目录,也会抛出空指针异常
都可以遍历到隐藏文件夹

//File file = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan\\08_FileAndRecursion\\1.txt"
//NullPointerException
//File file = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan\\08_Fi")
//NullPointerException
File file = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan\\08_FileAndRecursion");
String[] arr = file.list();
for (String fileName : arr) {
    System.out.println(fileName);
}

File file = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan\\08_FileAndRecursion");
File[] files = file.listFiles();
for (File f : files) {
    System.out.println(f);
}
文件过滤器

1)在File类中有两个和ListFiles重载的方法,方法的参数传递的就是过滤器
2)File[] listFiles(FileFilter filter)
java.io.FileFilter接口:用于抽象路径名(File对象)的过滤器。
作用:用来过滤文件(File对象)
抽象方法:用来过滤文件的方法
boolean accept(File pathname) 测试指定抽象路径名是否应该包含在某个路径名列表中。
参数:
File pathname:使用ListFiles方法遍历目录,得到的每一个文件对象
3)File[] listFiles(FilenameFilter filter)
java.io.FilenameFilter接口:实现此接口的类实例可用于过滤器文件名。
作用:用于过滤文件名称
抽象方法:用来过滤文件的方法
boolean accept(File dir, String name) 测试指定文件是否应该包含在某一文件列表中。
参数:
File dir:构造方法中传递的被遍历的目录
String name:使用ListFiles方法遍历目录,获取的每一个文件/文件夹的名称
注意:
两个过滤器接口是没有实现类的,需要我们自己写实现类,重写过滤的方法accept,在方法中自己定义过滤的规则
4)案例1:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G6e2bYEl-1603932138211)(/clipboard-1602688200638.png)]

在上述同样的需求中,不自己写实现类了,采用匿名内部类和Lambda表达式两种方式实现

/*
    定义一个方法,参数传递File类型的目录
    方法中对目录进行遍历
 */
public static void getAllFile(File dir){
    //传递过滤器对象 使用匿名内部类
    /*File[] files = dir.listFiles(new FileFilter() {
        @Override
        public boolean accept(File pathname) {
            //过滤规则,pathname是文件夹或者是.java结尾的文件返回true
            return pathname.isDirectory() || 
                        pathname.getName().toLowerCase().endsWith(".java");
        }
    });*/

    //使用Lambda表达式优化匿名内部类(接口中只有一个抽象方法)
    /*File[] files = dir.listFiles((File pathname)->{
        return pathname.isDirectory() ||
                     pathname.getName().toLowerCase().endsWith(".java");
    });*/

    File[] files = dir.listFiles(pathname->pathname.isDirectory() ||
                     pathname.getName().toLowerCase().endsWith(".java"));

    /*File[] files = dir.listFiles(new FilenameFilter() {
        @Override
        public boolean accept(File dir, String name) {
            //dir是要遍历的目录 name是目录中每一个文件/文件夹的名称
            //过滤规则,pathname是文件夹或者是.java结尾的文件返回true
            return new File(dir,name).isDirectory() || 
                                    name.toLowerCase().endsWith(".java");
        }
    });*/

    //使用Lambda表达式优化匿名内部类(接口中只有一个抽象方法)
    /*File[] files = dir.listFiles((File d, String name)->{
        //过滤规则,pathname是文件夹或者是.java结尾的文件返回true
        return new File(d,name).isDirectory() ||
                                      name.toLowerCase().endsWith(".java");
    });*/

    //File[] files = dir.listFiles((d,name)->new File(d,name).isDirectory()|| name.toLowerCase().endsWith(".java"));

    for (File f : files) {
        //对遍历得到的File对象f进行判断,判断是否是文件夹
        if(f.isDirectory()){
            //f是一个文件夹,则继续遍历这个文件夹
            //我们发现getAllFile方法就是传递文件夹,遍历文件夹的方法
            //所以直接调用getAllFile方法即可:递归(自己调用自己)
            getAllFile(f);
        }else{
            //f是一个文件,直接打印即可
            System.out.println(f);
        }
    }
}

IO流

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HdRYKlJN-1603932138211)(/clipboard-1602689105799.png)]

字节流
字节输出流【OutputStream】

1)java.io.OutputStream 抽象类是表示字节输出流的所有类的超类,将指定的字节信息写出到目的地。它定义了字节输出流的基本共性功能方法

public void close() :关闭此输出流并释放与此流相关联的任何系统资源。

public void flush() :刷新此输出流并强制任何缓冲的输出字节被写出。

public void write(byte[] b):将 b.length字节从指定的字节数组写入此输出流。

public void write(byte[] b, int off, int len) :从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。

public abstract void write(int b) :将指定的字节输出流。

文件字节输出流【FileOutputStream】

1)java.io.FileOutputStream extends OutputStream
作用:把内存中的数据写入到硬盘的文件中
2)构造方法:
FileOutputStream(String name)创建一个向具有指定名称的文件中写入数据的输出文件流。
FileOutputStream(File file) 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
参数:写入数据的目的地
String name:目的地是一个文件的路径
File file:目的地是一个文件
构造方法的作用:
1.创建一个FileOutputStream对象
2.会根据构造方法中传递的文件/文件路径,创建一个空的文件
3.会把FileOutputStream对象指向创建好的文件
3)写入数据的原理(内存–>硬盘):
java程序–>JVM(java虚拟机)–>OS(操作系统)–>OS调用写数据的方法–>把数据写入到文件中
4)文件字节输出流的使用步骤(重点):
1.创建一个FileOutputStream对象,构造方法中传递写入数据的目的地
2.调用FileOutputStream对象中的方法write,把数据写入到文件中
3.释放资源(流使用会占用一定的内存,使用完毕要把内存清空,提供程序的效率)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GsIpZhUu-1603932138212)(/clipboard-1602689651706.png)]

5) 一次写多个字节的方法:

public void write(byte[] b):将 b.length字节从指定的字节数组写入此输出流。
public void write(byte[] b, int off, int len) :从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。

//创建FileOutputStream对象,构造方法中绑定要写入数据的目的地
FileOutputStream fos = new FileOutputStream(new File("09_IOAndProperties\\b.txt"));
//调用FileOutputStream对象中的方法write,把数据写入到文件中
//在文件中显示100,写个字节
fos.write(49);
fos.write(48);
fos.write(48);

/*
    public void write(byte[] b):将 b.length字节从指定的字节数组写入此输出流。
    一次写多个字节:
        如果写的第一个字节是正数(0-127),那么显示的时候会查询ASCII表
        如果写的第一个字节是负数,那第一个字节会和第二个字节,两个字节组成一个中文显示,查询系统默认码表(GBK)
 */
byte[] bytes = {65,66,67,68,69};//ABCDE
//byte[] bytes = {-65,-66,-67,68,69};//烤紻E
fos.write(bytes);

/*
    public void write(byte[] b, int off, int len) :把字节数组的一部分写入到文件中
        int off:数组的开始索引
        int len:写几个字节
 */
fos.write(bytes,1,2);//BC

/*
    写入字符的方法:可以使用String类中的方法把字符串,转换为字节数组
        byte[] getBytes()  把字符串转换为字节数组
 */
byte[] bytes2 = "你好".getBytes();//utf-8中,3个字节是一个中文
System.out.println(Arrays.toString(bytes2));
//[-28, -67, -96, -27, -91, -67]
fos.write(bytes2);

//释放资源
fos.close();

6) 追加写/续写:
使用两个参数的构造方法
FileOutputStream(String name, boolean append)创建一个向具有指定 name 的文件中写入数据的输出文件流。FileOutputStream(File file, boolean append) 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
参数:
String name,File file:写入数据的目的地
boolean append:追加写开关
true:创建对象不会覆盖源文件,继续在文件的末尾追加写数据
false:创建一个新文件,覆盖源文件
7)写换行:写换行符号
windows:\r\n
linux:/n
mac:/r

FileOutputStream fos = new FileOutputStream("09_IOAndProperties\\c.txt",true);
for (int i = 1; i <=10 ; i++) {
    fos.write("你好".getBytes());
    fos.write("\r\n".getBytes());
}

fos.close();
字节输入流【InputStream】

1)java.io.InputStream 抽象类是表示字节输入流的所有类的超类,可以读取字节信息到内存中。它定义了字节输入流的基本共性功能方法。
public void close() :关闭此输入流并释放与此流相关联的任何系统资源。
public abstract int read() : 从输入流读取数据的下一个字节。
public int read(byte[] b) : 从输入流中读取一些字节数,并将它们存储到字节数组 b中

文件字节输入流【FileInputStream】

1)java.io.FileInputStream extends InputStream
作用:把硬盘文件中的数据,读取到内存中使用
2)构造方法:
FileInputStream(String name)
FileInputStream(File file)
参数:读取文件的数据源
String name:文件的路径
File file:文件
构造方法的作用:
1.会创建一个FileInputStream对象
2.会把FileInputStream对象指定构造方法中要读取的文件
3) 读取数据的原理(硬盘–>内存):
java程序–>JVM–>OS–>OS读取数据的方法–>读取文件
4)字节输入流的使用步骤(重点):
1.创建FileInputStream对象,构造方法中绑定要读取的数据源
2.使用FileInputStream对象中的方法read,读取文件
3.释放资源

//1.创建FileInputStream对象,构造方法中绑定要读取的数据源
FileInputStream fis = new FileInputStream("09_IOAndProperties\\c.txt");
//2.使用FileInputStream对象中的方法read,读取文件
//int read()读取文件中的一个字节并返回,读取到文件的末尾返回-1
/*int len = fis.read();
System.out.println(len);//97 a

len = fis.read();
System.out.println(len);// 98 b

len = fis.read();
System.out.println(len);//99 c

len = fis.read();
System.out.println(len);//-1

len = fis.read();
System.out.println(len);//-1*/

/*
    发现以上读取文件是一个重复的过程,所以可以使用循环优化
    不知道文件中有多少字节,使用while循环
    while循环结束条件,读取到-1的时候结束

    布尔表达式(len = fis.read())!=-1
        1.fis.read():读取一个字节
        2.len = fis.read():把读取到的字节赋值给变量len
        3.(len = fis.read())!=-1:判断变量len是否不等于-1
 */
int len = 0; //记录读取到的字节
while((len = fis.read())!=-1){
    System.out.print(len);//abc
}

//3.释放资源
fis.close();

5)字节输入流一次读取多个字节的方法:
int read(byte[] b) 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
明确两件事情:
1.方法的参数byte[]的作用?
起到缓冲作用,存储每次读取到的多个字节
数组的长度一把定义为1024(1kb)或者1024的整数倍
2.方法的返回值int是什么?
每次读取的有效字节个数
补充: String类的构造方法
String(byte[] bytes) :把字节数组转换为字符串
String(byte[] bytes, int offset, int length) 把字节数组的一部分转换为字符串 offset:数组的开始索引 length:转换的字节个数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GRldgZlp-1603932138212)(/clipboard-1602726789644.png)]

//创建FileInputStream对象,构造方法中绑定要读取的数据源
FileInputStream fis = new FileInputStream("09_IOAndProperties\\b.txt");
//使用FileInputStream对象中的方法read读取文件
//int read(byte[] b) 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
byte[] bytes = new byte[2];
int len = fis.read(bytes);
System.out.println(len);//2
//System.out.println(Arrays.toString(bytes));//[65, 66]
System.out.println(new String(bytes));//AB

len = fis.read(bytes);
System.out.println(len);//2
System.out.println(new String(bytes));//CD

len = fis.read(bytes);
System.out.println(len);//1
System.out.println(new String(bytes));//ED

len = fis.read(bytes);
System.out.println(len);//-1
System.out.println(new String(bytes));//ED*/

/*
    发现以上读取时一个重复的过程,可以使用循环优化
    不知道文件中有多少字节,所以使用while循环
    while循环结束的条件,读取到-1结束
 */
byte[] bytes = new byte[1024];//存储读取到的多个字节
int len = 0; //记录每次读取的有效字节个数
while((len = fis.read(bytes))!=-1){
    //String(byte[] bytes, int offset, int length) 
    //把字节数组的一部分转换为字符串 offset:数组的开始索引 length:转换的字节个数
    System.out.println(new String(bytes,0,len));//ABCDE
    //不直接new String,是因为这样做的话,后面会有一堆没有用的空格。
}
//释放资源
fis.close();

6)文件复制案例
明确:
数据源: c:\1.jpg
数据的目的地: d:\1.jpg
文件复制的步骤:
1.创建一个字节输入流对象,构造方法中绑定要读取的数据源
2.创建一个字节输出流对象,构造方法中绑定要写入的目的地
3.使用字节输入流对象中的方法read读取文件
4.使用字节输出流中的方法write,把读取到的字节写入到目的地的文件中
5.释放资源

long s = System.currentTimeMillis();
//1.创建一个字节输入流对象,构造方法中绑定要读取的数据源
FileInputStream fis = new FileInputStream("c:\\1.jpg");
//2.创建一个字节输出流对象,构造方法中绑定要写入的目的地
FileOutputStream fos = new FileOutputStream("d:\\1.jpg");
//一次读取一个字节写入一个字节的方式
//3.使用字节输入流对象中的方法read读取文件
int len = 0;
while((len = fis.read())!=-1){
    //4.使用字节输出流中的方法write,把读取到的字节写入到目的地的文件中
    fos.write(len);
}

//使用数组缓冲读取多个字节,写入多个字节
byte[] bytes = new byte[1024];
//3.使用字节输入流对象中的方法read读取文件
int len = 0;//每次读取的有效字节个数
while((len = fis.read(bytes))!=-1){
    //4.使用字节输出流中的方法write,把读取到的字节写入到目的地的文件中
    fos.write(bytes,0,len);
}

//5.释放资源(先关写的,后关闭读的;如果写完了,肯定读取完毕了)
fos.close();
fis.close();
long e = System.currentTimeMillis();
System.out.println("复制文件共耗时:"+(e-s)+"毫秒");
字符流

为什么要有字符流?
当使用字节流读取文本文件时,可能会有一个小问题。就是遇到中文字符时,可能不会显示完整的字符,那是因为 一个中文字符可能占用多个字节存储。所以Java提供一些字符流类,以字符为单位读写数据,专门用于处理文本文件。
使用字节流读取中文文件
1个中文 ---- GBK:占用两个字节 — UTF-8:占用3个字节

字符输入流【Reader】

1)java.io.Reader 抽象类是表示用于读取字符流的所有类的超类,可以读取字符信息到内存中。它定义了字符输入流的基本共性功能方法。
public void close() :关闭此流并释放与此流相关联的任何系统资源。
public int read() : 从输入流读取一个字符。
public int read(char[] cbuf) : 从输入流中读取一些字符,并将它们存储到字符数组 cbuf中

文件字符输入流【FileReader】

1)java.io.FileReader extends InputStreamReader extends Reader
作用:把硬盘文件中的数据以字符的方式读取到内存中
2)构造方法:
FileReader(String fileName)
FileReader(File file)
参数:读取文件的数据源
String fileName:文件的路径
File file:一个文件
FileReader构造方法的作用:
1.创建一个FileReader对象
2.会把FileReader对象指向要读取的文件
3)字符输入流的使用步骤:
1.创建FileReader对象,构造方法中绑定要读取的数据源
2.使用FileReader对象中的方法read读取文件
3.释放资源

//1.创建FileReader对象,构造方法中绑定要读取的数据源
FileReader fr = new FileReader("09_IOAndProperties\\c.txt");
//2.使用FileReader对象中的方法read读取文件
//int read() 读取单个字符并返回。
int len = 0;
while((len = fr.read())!=-1){
    System.out.print((char)len);
}

//int read(char[] cbuf)一次读取多个字符,将字符读入数组。
char[] cs = new char[1024];//存储读取到的多个字符
int len = 0;//记录的是每次读取的有效字符个数
while((len = fr.read(cs))!=-1){
    /*
        String类的构造方法
        String(char[] value) 把字符数组转换为字符串
        String(char[] value, int offset, int count) 
        把字符数组的一部分转换为字符串 offset数组的开始索引 count转换的个数
     */
    System.out.println(new String(cs,0,len));
}

//3.释放资源
fr.close();
字符输出流【Writer】

1)java.io.Writer 抽象类是表示用于写出字符流的所有类的超类,将指定的字符信息写出到目的地。它定义了字节 输出流的基本共性功能方法。
void write(int c) 写入单个字符。
void write(char[] cbuf) 写入字符数组。
abstract void write(char[] cbuf, int off, int len) 写入字符数组的某一部分,off数组的开始索引,len 写的字符个数。
void write(String str) 写入字符串。
void write(String str, int off, int len) 写入字符串的某一部分,off字符串的开始索引,len写的字符个 数。
void flush() 刷新该流的缓冲。
void close() 关闭此流,但要先刷新它

文件字符输出流【FileWriter】

1)java.io.FileWriter 类是写出字符到文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。
2)java.io.FileWriter extends OutputStreamWriter extends Writer
作用:把内存中字符数据写入到文件中
3)构造方法:
FileWriter(File file)根据给定的 File 对象构造一个 FileWriter 对象。
FileWriter(String fileName) 根据给定的文件名构造一个 FileWriter 对象。
参数:写入数据的目的地
String fileName:文件的路径
File file:是一个文件
构造方法的作用:
1.会创建一个FileWriter对象
2.会根据构造方法中传递的文件/文件的路径,创建文件
3.会把FileWriter对象指向创建好的文件
4)字符输出流的使用步骤(重点):
1.创建FileWriter对象,构造方法中绑定要写入数据的目的地
2.使用FileWriter中的方法write,把数据写入到内存缓冲区中(字符转换为字节的过程)
3.使用FileWriter中的方法flush,把内存缓冲区中的数据,刷新到文件中
4.释放资源(会先把内存缓冲区中的数据刷新到文件中)

//1.创建FileWriter对象,构造方法中绑定要写入数据的目的地
FileWriter fw = new FileWriter("09_IOAndProperties\\d.txt");
//2.使用FileWriter中的方法write,把数据写入到内存缓冲区中(字符转换为字节的过程)
//void write(int c) 写入单个字符。
fw.write(97);
//3.使用FileWriter中的方法flush,把内存缓冲区中的数据,刷新到文件中
fw.flush();  //如果不写这个,一定要写close
//4.释放资源(会先把内存缓冲区中的数据刷新到文件中)
fw.close();

5)flush方法和close方法的区别

​ flush :刷新缓冲区,流对象可以继续使用。
​ close: 先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了。

6)字符输出流写数据的其他方法

void write(char[] cbuf)写入字符数组。
stract void write(char[] cbuf, int off, int len)写入字符数组的某一部分,off数组的开始索引,len写的字符个数。

void write(String str)写入字符串。
void write(String str, int off, int len) 写入字符串的某一部分,off字符串的开始索引,len写的字符个数。

FileWriter fw = new FileWriter("09_IOAndProperties\\f.txt");
char[] cs = {'a','b','c','d','e'};
//void write(char[] cbuf)写入字符数组。
fw.write(cs);//abcde
//void write(char[] cbuf, int off, int len)
//写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
fw.write(cs,1,3);//bcd
//void write(String str)写入字符串。
fw.write("传智播客");//传智播客
//void write(String str, int off, int len) 
//写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
fw.write("黑马程序员",2,3);//程序员
fw.close();

7)续写和换行
续写,追加写:使用两个参数的构造方法
FileWriter(String fileName, boolean append)
FileWriter(File file, boolean append)
参数:
String fileName,File file:写入数据的目的地
boolean append:续写开关 true:不会创建新的文件覆盖源文件,可以续写; false:创建新的文件覆盖源文件
换行:换行符号 windows:\r\n linux:/n mac:/r

FileWriter fw = new FileWriter("09_IOAndProperties\\g.txt",true);
for (int i = 0; i <10 ; i++) {
    fw.write("HelloWorld"+i+"\r\n");
}
fw.close();

IO异常的处理

1.在jdk1.7之前使用try catch finally 处理流中的异常
格式:
try{
可能会产出异常的代码
}catch(异常类变量 变量名){
异常的处理逻辑
}finally{
一定会指定的代码
资源释放
}

//提高变量fw的作用域,让finally可以使用
//变量在定义的时候,可以没有值,但是使用的时候必须有值
//fw = new FileWriter("09_IOAndProperties\\g.txt",true);
                                        // 执行失败,fw没有值,fw.close会报错
FileWriter fw = null;
try{
    //可能会产出异常的代码
    fw = new FileWriter("w:\\09_IOAndProperties\\g.txt",true);
    for (int i = 0; i <10 ; i++) {
        fw.write("HelloWorld"+i+"\r\n");
    }
}catch(IOException e){
    //异常的处理逻辑
    System.out.println(e);
}finally {
    //一定会指定的代码
    //创建对象失败了,fw的默认值就是null,null是不能调用方法的,
    //会抛出NullPointerException,需要增加一个判断,不是null在把资源释放
    if(fw!=null){
        try {
            //fw.close方法声明抛出了IOException异常对象,
            //所以我们就的处理这个异常对象,要么throws,要么try catch
            fw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2.JDK7的新特性:
在try的后边可以增加一个(),在括号中可以定义流对象,那么这个流对象的作用域就在try中有效
try中的代码执行完毕,会自动把流对象释放,不用写finally
格式:
try(定义流对象;定义流对象…){
可能会产出异常的代码
}catch(异常类变量 变量名){
异常的处理逻辑
}

try(//1.创建一个字节输入流对象,构造方法中绑定要读取的数据源
    FileInputStream fis = new FileInputStream("c:\\1.jpg");
    //2.创建一个字节输出流对象,构造方法中绑定要写入的目的地
    FileOutputStream fos = new FileOutputStream("d:\\1.jpg");){

    //可能会产出异常的代码
    //一次读取一个字节写入一个字节的方式
    //3.使用字节输入流对象中的方法read读取文件
    int len = 0;
    while((len = fis.read())!=-1){
        //4.使用字节输出流中的方法write,把读取到的字节写入到目的地的文件中
        fos.write(len);
    }
}catch (IOException e){
    //异常的处理逻辑
    System.out.println(e);
}

3.JDK9新特性
try的前边可以定义流对象
在try后边的()中可以直接引入流对象的名称(变量名)
在try代码执行完毕之后,流对象也可以释放掉,不用写finally
格式:
A a = new A();
B b = new B();
try(a,b){
可能会产出异常的代码
}catch(异常类变量 变量名){
异常的处理逻辑
}
注意:不要忘了处理new对象的异常

public static void main(String[] args) throws IOException {
    //1.创建一个字节输入流对象,构造方法中绑定要读取的数据源
    FileInputStream fis = new FileInputStream("c:\\1.jpg");
    //2.创建一个字节输出流对象,构造方法中绑定要写入的目的地
    FileOutputStream fos = new FileOutputStream("d:\\1.jpg");
    try(fis;fos){
        //一次读取一个字节写入一个字节的方式
        //3.使用字节输入流对象中的方法read读取文件
        int len = 0;
        while((len = fis.read())!=-1){
            //4.使用字节输出流中的方法write,把读取到的字节写入到目的地的文件中
            fos.write(len);
        }
    }catch (IOException e){
        System.out.println(e);
    }
    //fos.write(1);//Stream Closed
}

属性集Properties

1.java.util.Properties集合 extends Hashtable<k,v> implements Map<k,v>
2.Properties 类表示了一个持久的属性集。Properties 可保存在流中或从流中加载。
3.Properties集合是一个唯一和IO流相结合的集合
可以使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储
可以使用Properties集合中的方法load,把硬盘中保存的文件(键值对),读取到集合中使用
4.属性列表中每个键及其对应值都是一个字符串。
Properties集合是一个双列集合,key和value默认都是字符串
正因为默认都是字符串,所以用的时候就不需要再写泛型了。
5.Properties集合有一些操作字符串的特有方法
Object setProperty(String key, String value) 调用 Hashtable 的方法 put。
String getProperty(String key) 通过key找到value值,此方法相当于Map集合中的get(key)方法
Set stringPropertyNames() 返回此属性列表中的键集,其中该键及其对应值是字符串,此方法相当于Map集合中的keySet方法。

//创建Properties集合对象
Properties prop = new Properties();
//使用setProperty往集合中添加数据
prop.setProperty("赵丽颖","168");
prop.setProperty("迪丽热巴","165");
prop.setProperty("古力娜扎","160");
//prop.put(1,true);

//使用stringPropertyNames把Properties集合中的键取出,存储到一个Set集合中
Set<String> set = prop.stringPropertyNames();

//遍历Set集合,取出Properties集合的每一个键
for (String key : set) {
    //使用getProperty方法通过key获取value
    String value = prop.getProperty(key);
    System.out.println(key+"="+value);
}

6.可以使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储
void store(OutputStream out, String comments)
void store(Writer writer, String comments)
参数:
OutputStream out:字节输出流,不能写入中文
Writer writer:字符输出流,可以写中文
String comments:注释,用来解释说明保存的文件是做什么用的
不能使用中文,会产生乱码,默认是Unicode编码
一般使用""空字符串
使用步骤:
1.创建Properties集合对象,添加数据
2.创建字节输出流/字符输出流对象,构造方法中绑定要输出的目的地
3.使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储
4.释放资源

//1.创建Properties集合对象,添加数据
Properties prop = new Properties();
prop.setProperty("赵丽颖","168");
prop.setProperty("迪丽热巴","165");
prop.setProperty("古力娜扎","160");

//2.创建字节输出流/字符输出流对象,构造方法中绑定要输出的目的地
//FileWriter fw = new FileWriter("09_IOAndProperties\\prop.txt");

//3.使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储
//prop.store(fw,"save data");

//4.释放资源
//fw.close();

//使用字节流 ---匿名对象使用完自己就关了,不需要我们再close
prop.store(new FileOutputStream("09_IOAndProperties\\prop2.txt"),"");
//因为此时输出的是中文,所以在文本中的结果是乱码  

7.可以使用Properties集合中的方法load,把硬盘中保存的文件(键值对),读取到集合中使用
void load(InputStream inStream)
void load(Reader reader)
参数:
InputStream inStream:字节输入流,不能读取含有中文的键值对
Reader reader:字符输入流,能读取含有中文的键值对
使用步骤:
1.创建Properties集合对象
2.使用Properties集合对象中的方法load读取保存键值对的文件
3.遍历Properties集合
注意:
1.存储键值对的文件中,键与值默认的连接符号可以使用=,空格(其他符号)
2.存储键值对的文件中,可以使用#进行注释,被注释的键值对不会再被读取
3.存储键值对的文件中,键与值默认都是字符串,不用再加引号

//1.创建Properties集合对象
Properties prop = new Properties();
//2.使用Properties集合对象中的方法load读取保存键值对的文件
prop.load(new FileReader("09_IOAndProperties\\prop.txt"));
//prop.load(new FileInputStream("09_IOAndProperties\\prop.txt"));
//3.遍历Properties集合
Set<String> set = prop.stringPropertyNames();
for (String key : set) {
    String value = prop.getProperty(key);
   System.out.println(key+"="+value);
}

缓冲流

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WgFHoy9p-1603932138213)(/clipboard-1602730305688.png)]

字节缓冲输出流【BufferedOutputStream】

1)java.io.BufferedOutputStream extends OutputStream
2)继承自父类的共性成员方法:

public void close() :关闭此输出流并释放与此流相关联的任何系统资源。

public void flush() :刷新此输出流并强制任何缓冲的输出字节被写出。

public void write(byte[] b):将 b.length字节从指定的字节数组写入此输出流。

public void write(byte[] b, int off, int len) :从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。

public abstract void write(int b) :将指定的字节输出流。
3)构造方法:
BufferedOutputStream(OutputStream out) 创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
BufferedOutputStream(OutputStream out, int size) 创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流。
参数:
OutputStream out:字节输出流
我们可以传递FileOutputStream,缓冲流会给FileOutputStream增加一个缓冲区,提高FileOutputStream的写入效率
int size:指定缓冲流内部缓冲区的大小,不指定默认
使用步骤(重点)
1.创建FileOutputStream对象,构造方法中绑定要输出的目的地
2.创建BufferedOutputStream对象,构造方法中传递FileOutputStream对象对象,提高FileOutputStream对象效率
3.使用BufferedOutputStream对象中的方法write,把数据写入到内部缓冲区中
4.使用BufferedOutputStream对象中的方法flush,把内部缓冲区中的数据,刷新到文件中
5.释放资源(会先调用flush方法刷新数据,第4部可以省略)

//1.创建FileOutputStream对象,构造方法中绑定要输出的目的地
FileOutputStream fos = new FileOutputStream("10_IO\\a.txt");
//2.创建BufferedOutputStream对象,构造方法中传递FileOutputStream对象对象,提高FileOutputStream对象效率
BufferedOutputStream bos = new BufferedOutputStream(fos);
//3.使用BufferedOutputStream对象中的方法write,把数据写入到内部缓冲区中
bos.write("我把数据写入到内部缓冲区中".getBytes());
//4.使用BufferedOutputStream对象中的方法flush,把内部缓冲区中的数据,刷新到文件中
bos.flush();
//5.释放资源(会先调用flush方法刷新数据,第4部可以省略)
bos.close();
字节缓冲输入流【BufferedInputStream】

1)java.io.BufferedInputStream extends InputStream
2)继承自父类的成员方法:
int read()从输入流中读取数据的下一个字节。
int read(byte[] b) 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
void close() 关闭此输入流并释放与该流关联的所有系统资源。
3)构造方法:
BufferedInputStream(InputStream in) 创建一个 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。
BufferedInputStream(InputStream in, int size) 创建具有指定缓冲区大小的 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。
参数:
InputStream in:字节输入流
我们可以传递FileInputStream,缓冲流会给FileInputStream增加一个缓冲区,提高FileInputStream的读取效率
int size:指定缓冲流内部缓冲区的大小,不指定默认
4)使用步骤(重点):
1.创建FileInputS tream对象,构造方法中绑定要读取的数据源
2.创建BufferedInputStream对象,构造方法中传递FileInputStream对象,提高FileInputStream对象的读取效率
3.使用BufferedInputStream对象中的方法read,读取文件
4.释放资源

//1.创建FileInputStream对象,构造方法中绑定要读取的数据源
FileInputStream fis = new FileInputStream("10_IO\\a.txt");
//2.创建BufferedInputStream对象,构造方法中传递FileInputStream对象,提高FileInputStream对象的读取效率
BufferedInputStream bis = new BufferedInputStream(fis);
//3.使用BufferedInputStream对象中的方法read,读取文件
//int read()从输入流中读取数据的下一个字节。
int len = 0;//记录每次读取到的字节
while((len = bis.read())!=-1){
    System.out.println(len);
}

//int read(byte[] b) 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
byte[] bytes =new byte[1024];//存储每次读取的数据
int len = 0; //记录每次读取的有效字节个数
while((len = bis.read(bytes))!=-1){
    System.out.println(new String(bytes,0,len));
}
//4.释放资源
bis.close();
字符缓冲输出流【BufferedWriter】

1)java.io.BufferedWriter extends Writer
2)继承自父类的共性成员方法:

void write(int c) 写入单个字符。
void write(char[] cbuf)写入字符数组。

abstract void write(char[] cbuf, int off, int len)写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
void write(String str)写入字符串。

void write(String str, int off, int len) 写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
void flush()刷新该流的缓冲。

void close() 关闭此流,但要先刷新它。
3)构造方法:
BufferedWriter(Writer out) 创建一个使用默认大小输出缓冲区的缓冲字符输出流。
BufferedWriter(Writer out, int sz) 创建一个使用给定大小输出缓冲区的新缓冲字符输出流。
参数:
Writer out:字符输出流
我们可以传递FileWriter,缓冲流会给FileWriter增加一个缓冲区,提高FileWriter的写入效率
int sz:指定缓冲区的大小,不写默认大小
4)特有的成员方法:
void newLine() 写入一个行分隔符。会根据不同的操作系统,获取不同的行分隔符
换行:换行符号 windows:\r\n linux:/n mac:/r
5)使用步骤:
1.创建字符缓冲输出流对象,构造方法中传递字符输出流
2.调用字符缓冲输出流中的方法write,把数据写入到内存缓冲区中
3.调用字符缓冲输出流中的方法flush,把内存缓冲区中的数据,刷新到文件中
4.释放资源

//1.创建字符缓冲输出流对象,构造方法中传递字符输出流
BufferedWriter bw = new BufferedWriter(new FileWriter("10_IO\\c.txt"));
//2.调用字符缓冲输出流中的方法write,把数据写入到内存缓冲区中
for (int i = 0; i <10 ; i++) {
    bw.write("传智播客");
    //bw.write("\r\n");
    bw.newLine();
}
//3.调用字符缓冲输出流中的方法flush,把内存缓冲区中的数据,刷新到文件中
bw.flush();
//4.释放资源
bw.close();
字符缓冲输入流【BufferedReader】

1)java.io.BufferedReader extends Reader
2)继承自父类的共性成员方法:
int read() 读取单个字符并返回。
int read(char[] cbuf)一次读取多个字符,将字符读入数组。
void close() 关闭该流并释放与之关联的所有资源。
3)构造方法:
BufferedReader(Reader in) 创建一个使用默认大小输入缓冲区的缓冲字符输入流。
BufferedReader(Reader in, int sz) 创建一个使用指定大小输入缓冲区的缓冲字符输入流。
参数:
Reader in:字符输入流
我们可以传递FileReader,缓冲流会给FileReader增加一个缓冲区,提高FileReader的读取效率
4)特有的成员方法:
String readLine() 读取一个文本行。读取一行数据
行的终止符号:通过下列字符之一即可认为某行已终止:换行 (’\n’)、回车 (’\r’) 或回车后直接跟着换行(\r\n)。
返回值:
包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null
5)使用步骤:
1.创建字符缓冲输入流对象,构造方法中传递字符输入流
2.使用字符缓冲输入流对象中的方法read/readLine读取文本
3.释放资源

//1.创建字符缓冲输入流对象,构造方法中传递字符输入流
BufferedReader br = new BufferedReader(new FileReader("10_IO\\c.txt"));

//2.使用字符缓冲输入流对象中的方法read/readLine读取文本
String line = br.readLine();
System.out.println(line);

line = br.readLine();
System.out.println(line);

line = br.readLine();
System.out.println(line);

line = br.readLine();
System.out.println(line);

/*
    发下以上读取是一个重复的过程,所以可以使用循环优化
    不知道文件中有多少行数据,所以使用while循环
    while的结束条件,读取到null结束
 */
String line;
while((line = br.readLine())!=null){
    System.out.println(line);
}

//3.释放资源
br.close();

5)案例:

​ 对文本的内容进行排序,按照(1,2,3…)顺序排序

分析:
1.创建一个HashMap集合对象,key:存储每行文本的序号(1,2,3,…);value:存储每行的文本
2.创建字符缓冲输入流对象,构造方法中绑定字符输入流
3.创建字符缓冲输出流对象,构造方法中绑定字符输出流
4.使用字符缓冲输入流中的方法readline,逐行读取文本
5.对读取到的文本进行切割,获取行中的序号和文本内容
6.把切割好的序号和文本的内容存储到HashMap集合中(key序号是有序的,会自动排序1,2,3,4…)
7.遍历HashMap集合,获取每一个键值对
8.把每一个键值对,拼接为一个文本行
9.把拼接好的文本,使用字符缓冲输出流中的方法write,写入到文件中
10.释放资源

//1.创建一个HashMap集合对象,可以:存储每行文本的序号(1,2,3,..);//value:存储每行的文本
HashMap<String,String> map = new HashMap<>();
//2.创建字符缓冲输入流对象,构造方法中绑定字符输入流
BufferedReader br = new BufferedReader(new FileReader("10_IO\\in.txt"));
//3.创建字符缓冲输出流对象,构造方法中绑定字符输出流
BufferedWriter bw = new BufferedWriter(new FileWriter("10_IO\\out.txt"));
//4.使用字符缓冲输入流中的方法readline,逐行读取文本
String line;
while((line = br.readLine())!=null){
    //5.对读取到的文本进行切割,获取行中的序号和文本内容
    String[] arr = line.split("\\.");
    //6.把切割好的序号和文本的内容存储到HashMap集合中(key序号是有序的,会自动排序1,2,3,4..)
    map.put(arr[0],arr[1]);
}

//7.遍历HashMap集合,获取每一个键值对
for(String key : map.keySet()){
    String value = map.get(key);
    //8.把每一个键值对,拼接为一个文本行
    line = key + "." + value;
    //9.把拼接好的文本,使用字符缓冲输出流中的方法write,写入到文件中
    bw.write(line);
    bw.newLine();//写换行
}
//10.释放资源
bw.close();
br.close();

转换流

1.字符编码和字符集
1)字符编码 Character Encoding : 就是一套自然语言的字符与二进制数之间的对应规则
2)字符集 Charset :也叫编码表。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符 号、数字等
3)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uxYgZVa2-1603932138214)(/clipboard-1602735021285.png)]

4)ASCII字符集:基本的ASCII字符集,使用7位(bits)表示一个字符,共128字符。ASCII的扩展字符集使用8位(bits) 表示一个字符,共256字符,方便支持欧洲常用字符
5)GBK:最常用的中文码表。是在GB2312标准基础上的扩展规范,使用了双字节编码方案,共收录了 21003个汉字,完全兼容GB2312标准,同时支持繁体汉字以及日韩汉字等。
6)UTF-8:1. 128个US-ASCII字符,只需一个字节编码。 2. 拉丁文等字符,需要二个字节编码。 3. 大部分常用字(含中文),使用三个字节编码。 4. 其他极少使用的Unicode辅助字符,使用四字节编码
2.编码所导致的问题:
1)FileReader可以读取IDE默认编码格式(UTF-8)的文件
FileReader读取系统默认编码(中文GBK)会产生乱码���

FileReader fr = new FileReader("10_IO\\我是GBK格式的文本.txt");
int len = 0;
while((len = fr.read())!=-1){
    System.out.print((char)len);
}
fr.close();

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3T1tsgig-1603932138214)(/clipboard-1602736134140.png)]

字符流通向字节流的桥梁【OutputStreamWriter】

1)java.io.OutputStreamWriter extends Writer
作用:可使用指定的 charset 将要写入流中的字符编码成字节。(编码:把能看懂的变成看不懂)
2)继续自父类的共性成员方法:

void write(int c) 写入单个字符。
void write(char[] cbuf)写入字符数组。

abstract void write(char[] cbuf, int off, int len)写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
void write(String str)写入字符串。

void write(String str, int off, int len) 写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
id flush()刷新该流的缓冲。

void close() 关闭此流,但要先刷新它。
3)构造方法:
OutputStreamWriter(OutputStream out)创建使用默认字符编码的 OutputStreamWriter。
OutputStreamWriter(OutputStream out, String charsetName) 创建使用指定字符集的 OutputStreamWriter。
参数:
OutputStream out:字节输出流,可以用来写转换之后的字节到文件中
String charsetName:指定的编码表名称,不区分大小写,可以是utf-8/UTF-8,gbk/GBK,…不指定默认使用UTF-8
4)使用步骤:
1.创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称
2.使用OutputStreamWriter对象中的方法write,把字符转换为字节存储缓冲区中(编码)
3.使用OutputStreamWriter对象中的方法flush,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
4.释放资源

//1.创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("10_IO\\gbk.txt"),"GBK");
//2.使用OutputStreamWriter对象中的方法write,把字符转换为字节存储缓冲区中(编码)
osw.write("你好");
//3.使用OutputStreamWriter对象中的方法flush,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
osw.flush();
//4.释放资源
osw.close();
字节流通向字符流的桥梁【InputStreamReader】
  1. java.io.InputStreamReader extends Reader
    作用:使用指定的 charset 读取字节并将其解码为字符。(解码:把看不懂的变成能看懂的)
    2)继承自父类的共性成员方法:
    int read() 读取单个字符并返回。
    int read(char[] cbuf)一次读取多个字符,将字符读入数组。
    void close() 关闭该流并释放与之关联的所有资源。
    3)构造方法:
    InputStreamReader(InputStream in) 创建一个使用默认字符集的 InputStreamReader。
    InputStreamReader(InputStream in, String charsetName) 创建使用指定字符集的 InputStreamReader。
    参数:
    InputStream in:字节输入流,用来读取文件中保存的字节
    String charsetName:指定的编码表名称,不区分大小写,可以是utf-8/UTF-8,gbk/GBK,…不指定默认使用UTF-8
    4)使用步骤:
    1.创建InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称
    2.使用InputStreamReader对象中的方法read读取文件
    3.释放资源
    5)注意事项:
    构造方法中指定的编码表名称要和文件的编码相同,否则会发生乱码
//1.创建InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称
InputStreamReader isr = new InputStreamReader
                    (new FileInputStream("10_IO\\gbk.txt"),"GBK");//你好
//2.使用InputStreamReader对象中的方法read读取文件
int len = 0;
while((len = isr.read())!=-1){
    System.out.println((char)len);
}
//3.释放资源
isr.close();

5.小案例:转换文件编码
将GBK编码的文本文件,转换为UTF-8编码的文本文件。
分析:
1.创建InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称GBK
2.创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称UTF-8
3.使用InputStreamReader对象中的方法read读取文件
4.使用OutputStreamWriter对象中的方法write,把读取的数据写入到文件中
5.释放资源

//1.创建InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称GBK
InputStreamReader isr = new InputStreamReader
    		(new FileInputStream("10_IO\\我是GBK格式的文本.txt"),"GBK");
//2.创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称UTF-8
OutputStreamWriter osw = new OutputStreamWriter
           (new FileOutputStream("10_IO\\我是utf_8格式的文件.txt"),"UTF-8");
//3.使用InputStreamReader对象中的方法read读取文件
int len = 0;
while((len = isr.read())!=-1){//注意这里是一个字符一个字符读,而不是一个字节一个字节读
    //4.使用OutputStreamWriter对象中的方法write,把读取的数据写入到文件中
    osw.write(len);
}
//5.释放资源
osw.close();
isr.close();

序列化流

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ShHkIN0H-1603932138214)(/clipboard-1602737635021.png)]

要序列化类的要求
1)序列化和反序列化的时候,会抛出NotSerializableException没有序列化异常
2)类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。
Serializable接口也叫标记型接口
要进行序列化和反序列化的类必须实现Serializable接口,就会给类添加一个标记
当我们进行序列化和反序列化的时候,就会检测类上是否有这个标记
有:就可以序列化和反序列化
没有:就会抛出 NotSerializableException异常
去市场买肉–>肉上有一个蓝色章(检测合格)–>放心购买–>买回来怎么吃随意
3) static关键字:静态关键字
静态优先于非静态加载到内存中(静态优先于对象进入到内存中)
被static修饰的成员变量不能被序列化的,序列化的都是对象
private static int age;
oos.writeObject(new Person(“小美女”,18));
Object o = ois.readObject();
Person{name=‘小美女’, age=0}
4)transient关键字:瞬态关键字
被transient修饰成员变量,不能被序列化
private transient int age;
oos.writeObject(new Person(“小美女”,18));
Object o = ois.readObject();
Person{name=‘小美女’, age=0}

对象的序列化流【ObjectOutputStream】

1)java.io.ObjectOutputStream extends OutputStream
作用:把对象以流的方式写入到文件中保存
2)构造方法:
ObjectOutputStream(OutputStream out) 创建写入指定 OutputStream 的 ObjectOutputStream。
参数:
OutputStream out:字节输出流
3)特有的成员方法:
void writeObject(Object obj) 将指定的对象写入 ObjectOutputStream。
4)使用步骤:
1.创建ObjectOutputStream对象,构造方法中传递字节输出流
2.使用ObjectOutputStream对象中的方法writeObject,把对象写入到文件中
3.释放资源

//1.创建ObjectOutputStream对象,构造方法中传递字节输出流
ObjectOutputStream oos = new ObjectOutputStream
                              (new FileOutputStream("10_IO\\person.txt"));
//2.使用ObjectOutputStream对象中的方法writeObject,把对象写入到文件中
oos.writeObject(new Person("小美女",18));
//3.释放资源
oos.close();
对象的反序列化流【ObjectInputStream】

1)java.io.ObjectInputStream extends InputStream
作用:把文件中保存的对象,以流的方式读取出来使用
2)构造方法:
ObjectInputStream(InputStream in) 创建从指定 InputStream 读取的 ObjectInputStream。
参数:
InputStream in:字节输入流
3)特有的成员方法:
Object readObject() 从 ObjectInputStream 读取对象。
4)使用步骤:
1.创建ObjectInputStream对象,构造方法中传递字节输入流
2.使用ObjectInputStream对象中的方法readObject读取保存对象的文件
3.释放资源
4.使用读取出来的对象(打印)
5)注意:readObject方法声明抛出了ClassNotFoundException(class文件找不到异常)
当不存在对象的class文件时抛出此异常
反序列化的前提:
1.类必须实现Serializable
2.必须存在类对应的class文件

//1.创建ObjectInputStream对象,构造方法中传递字节输入流
ObjectInputStream ois = new ObjectInputStream
                                (new FileInputStream("10_IO\\person.txt"));
//2.使用ObjectInputStream对象中的方法readObject读取保存对象的文件
Object o = ois.readObject();
//3.释放资源
ois.close();
//4.使用读取出来的对象(打印)
System.out.println(o);
Person p = (Person)o;
System.out.println(p.getName()+p.getAge());

序列化冲突异常图解:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RSeU1Wu4-1603932138215)(/clipboard-1602740040235.png)]

5.小案例:序列化集合
当我们想在文件中保存多个对象的时候
可以把多个对象存储到一个集合中
对集合进序列化和反序列化
分析:
1.定义一个存储Person对象的ArrayList集合
2.往ArrayList集合中存储Person对象
3.创建一个序列化流ObjectOutputStream对象
4.使用ObjectOutputStream对象中的方法writeObject,对集合进行序列化
5.创建一个反序列化ObjectInputStream对象
6.使用ObjectInputStream对象中的方法readObject读取文件中保存的集合
7.把Object类型的集合转换为ArrayList类型
8.遍历ArrayList集合
9.释放资源

//1.定义一个存储Person对象的ArrayList集合
ArrayList<Person> list = new ArrayList<>();
//2.往ArrayList集合中存储Person对象
list.add(new Person("张三",18));
list.add(new Person("李四",19));
list.add(new Person("王五",20));
//3.创建一个序列化流ObjectOutputStream对象
ObjectOutputStream oos = new ObjectOutputStream
                                (new FileOutputStream("10_IO\\list.txt"));
//4.使用ObjectOutputStream对象中的方法writeObject,对集合进行序列化
oos.writeObject(list);
//5.创建一个反序列化ObjectInputStream对象
ObjectInputStream ois = new ObjectInputStream
                                  (new FileInputStream("10_IO\\list.txt"));
//6.使用ObjectInputStream对象中的方法readObject读取文件中保存的集合
Object o = ois.readObject();
//7.把Object类型的集合转换为ArrayList类型
ArrayList<Person> list2 = (ArrayList<Person>)o;
//8.遍历ArrayList集合
for (Person p : list2) {
    System.out.println(p);
}
//9.释放资源
ois.close();
oos.close();

打印流【PrintStream】

1.java.io.PrintStream:为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。
2.特点:
1)只负责数据的输出,不负责数据的读取
2)与其他输出流不同,PrintStream 永远不会抛出 IOException
3)有特有的方法,print,println
void print(任意类型的值)
void println(任意类型的值并换行)
3.构造方法:
PrintStream(File file):输出的目的地是一个文件
PrintStream(OutputStream out):输出的目的地是一个字节输出流
PrintStream(String fileName) :输出的目的地是一个文件路径
4.PrintStream extends OutputStream
继承自父类的成员方法:

public void close() :关闭此输出流并释放与此流相关联的任何系统资源。
public void flush() :刷新此输出流并强制任何缓冲的输出字节被写出。

public void write(byte[] b):将 b.length字节从指定的字节数组写入此输出流。
public void write(byte[] b, int off, int len) :从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。

public abstract void write(int b) :将指定的字节输出流。
5.注意:
如果使用继承自父类的write方法写数据,那么查看数据的时候会查询编码表 97->a
如果使用自己特有的方法print/println方法写数据,写的数据原样输出 97->97

//System.out.println("HelloWorld");

//创建打印流PrintStream对象,构造方法中绑定要输出的目的地
PrintStream ps = new PrintStream("10_IO\\print.txt");
//如果使用继承自父类的write方法写数据,那么查看数据的时候会查询编码表 97->a
ps.write(97);
//如果使用自己特有的方法print/println方法写数据,写的数据原样输出 97->97
ps.println(97);
ps.println(8.8);
ps.println('a');
ps.println("HelloWorld");
ps.println(true);

//释放资源
ps.close();

6.可以改变输出语句的目的地(打印流的流向)
输出语句,默认在控制台输出
使用System.setOut方法改变输出语句的目的地改为参数中传递的打印流的目的地
static void setOut(PrintStream out)
重新分配“标准”输出流。

System.out.println("我是在控制台输出");

PrintStream ps = new PrintStream("10_IO\\目的地是打印流.txt");
System.setOut(ps);//把输出语句的目的地改变为打印流的目的地
System.out.println("我在打印流的目的地中输出");

ps.close();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值