java学习笔记:全部,txt版本

笔者注:

1.不知道怎么上传附件,所以就把txt文本内容全部贴在这里吧。

2.已经把txt版本的笔记上传到CSDN了,我没有设置索要积分才能下载,但是不知道为什么CSDN下载这个资料会需要积分。

一、java的常量
1. 常量的定义:在程序执行过程中,其值不可以发生改变的量。
2. 常量的分类:
  (1) 字符串常量  被双引号包裹起来的字符串,如"hello world",""(这是空字符串)
  (2)整数常量 如12
  (3)小数常量 如12.34
  (4)字符常量 被单引号包裹起来的单个字符,如'a'
  (5)布尔常量 true,false  打印出来也是true,false
  (6)空常量 null
3.常量数据在转换时,按int型转换

二、java变量的数据类型
1. java是一种强类型语言,针对每一个数据都给出了明确的数据类型。
2.java变量数据类型的分类:分为基本数据类型、引用数据类型(类、接口、数组)。String属于类,不属于数据类型。
3. java变量基本数据类型的分类:四类八种
  (1)整型:byte 1个字节,short 2,int 4,long 8。默认是int型。
  (2)浮点型:float 4,double 8。默认是double型。注意:double,float类型的变量在做算数运算时,要注意有精度丢失的问题,如果做关系运算,就不需要考虑精度丢失的问题。
  (3)字符型:char 2,如'a'
  (4)布尔型:boolean 1
4. 变量的定义格式:
数据类型 变量名称=初始化值;
5.注意事项:定义long型的数据,末尾加L或l,建议加L;定义float型的数据,末尾加F或f,建议加F。
6.基本数据类型在类中的默认值
byte,short,int long的默认值为0零
float,double的默认值为0.0
char的默认值为'\u0000'优零
boolean的默认值为false
引用类型的默认值为null

三、标识符identifier
1. 定义:就是用来给包、类、变量、方法等起名字的符号
2.组成规则
  (1)unicode字符、数字字符、英文大小写字符、汉字(不建议使用汉字)
  (2)下划线_
  (3)美元符号$
3.注意事项:
  (1)不能以数字开头
  (2)不能是java的关键字
4.命名规范:
  (1)见名知意,最好用英文、汉语拼音
  (2)给类、接口命名,每个单词的首字母要大写,如MaxAge
  (3)给方法、变量命名,从第二个单词开始,首字母要大写,如maxAge
  (4)给常量命名,每个字母都大写,多个单词之间用下划线隔开,如MAX_AGE
  (5)给包命名,全部字母小写,多级包之间用"."隔开,一般用公司域名倒过来,如cctv.com的包名可以写成com.cctv
  (6)给项目命名:和类或者和方法的名称一致即可。

四、类型转换Type Conversion
1.分类:隐式转换、强制转换
2.隐式转换
  (1)byte, short, char ---> int --->long ---> float ---> double
  (2)boolean类型不参与这样的运算
  (3)注意:byte, short, char在做运算时,无论遇没遇到int,都会首先提升为int,再参与运算。
  (4)byte b = 3 + 4;//这里3和4都是常量,而常量会优先做运算,按int型计算编程7,再参与运算。
3.强制转换
  (1)格式:目标类型 变量名称 = (目标类型)(被转换的数据);
  (2)注意:虽然可以做强制转换,但是不建议,因为强制转换可能有数据的丢失。只有不得不转换,而且你确定不会出现问题的时候,才可以使用强制转换,否则不要强用。

五、写程序时,做数据测试,应该考虑这几种情况:(1)正确数据;(2)边界数据;(3)错误数据。

六、数组
1. 定义:数组是存储同一种数据类型的多个变量的容器。其中每一个变量叫做元素,元素的编号叫做索引。
2. 格式:
  (1)数据类型[] 数组名;//推荐用这个
  (2)数据类型 数组名[];
3. 
int[] arr;//定义了一个int类型的数据,数组名是arr。--推荐用这个
int arr[];//定义了一个int类型的变量,变量名是arr数组。
4.数组的初始化
  (1)数组的初始化,就是为数组开辟内存空间,并为数组中的每一个元素赋予初始值。
  (2)数组初始化的分类:动态初始化、静态初始化
  (3)动态初始化:只给出长度,由系统给出初始化值。
      格式:数据类型[] 数组名 = new 数据类型[数组长度];
      举例:int[] arr = new int[3];
左边:int:说明数组中的元素类型是int类型
         []:说明这是数组
         arr:这是数据的名称
右边:new:为数组分配内存空间
         int:说明数组中中的元素是int类型
         []:说明这是数组
         3:数组长度,就是数组中的元素个数
      赋值:数组名[索引]=数据;
      获取元素的格式:数组名[索引]
  (4)静态初始化:给出初始值,由系统决定长度。
      格式:数据类型[] 数组名 = new 数据类型[]{元素1,元素2,...};
      简化格式:数据类型[] 数组名 = {元素1,元素2,...};
5.如果两个数组拿的是同一个堆内存的地址,那么任何一个数组对堆内存数据进行了修改,另一个访问到的也是修改过的。例子:夫妻-娃
int[] arr_1=new int[3];
int[] arr_2=arr_1;//把arr_1数组的地址赋值给arr_2数组,这样两个数组都指向了同一个堆内存空间。

6.数组操作的常见问题
  (1)ArrayIndexOutOfBoundsException数组索引越界异常,因为访问了不存在的索引。
  (2)NullPointerException空指针异常,因为null是指不再指向堆内存的数据,而程序还要访问它。
      空常量null是可以复制给引用数据类型的,表示该引用不再指向堆内存的数据。
  (3)代码示例:
int[] arr = {1,2,3};
System.out.println(arr[3]);//java.lang.ArrayIndexOutOfBoundsException
arr = null;
System.out.println(arr[0]);//java.lang.NullPointerException

7.用数据的length属性获取数组元素个数,格式:数组名.length
例子:遍历数组 for(int i = 0; i < arr.length; i++){System.out.println(arr[i]);}
例子:获取数组最值
解题思路:
  1.找一个参照物,但是这个参照物只能是该数组中的元素,不能是外部的。
  2.拿数组中第一个元素作为参照物。max = arr[0]; min=arr[0];
  3.遍历数组,从第二个元素开始,依次与参照物比较,如果元素比max大,那么久留下来做参照物。if(max<arr[i]) max=arr[i]; if(min>arr[i]) min=arr[i];
  4.再比较下一个元素。最后输出结果。
例子:不死神兔
有一对兔子,从出生后第三个月起,每个月都生一对兔子,小兔子长到第三个月后,每个月又生一对兔子。假如兔子都不死,问第20个月,兔子对数是多少?
解题思路:
先找规律
规律  对数
第一个月  1
二  1
三  2
四  3
五  5
……
找出的规律为:从第三个月开始,每个月的兔子对数都是前两个月兔子对数相加之和,第一个月和第二个月的兔子对数都是1。
分析:
  1.由于数据比较多,所以我们定义数组实现。int[] arr=new int[20];
  2.给数据元素赋值。arr[0]=1;arr[1]=1;
  3.从第三个月开始,根据规律赋值。for(int i=2;i < length;i++)arr[i]=arr[i-1]+arr[i-2];

8.数组常用的方法
import java.util.Arrays;
Arrays.sort(arr);//可以对数组从小到大排序,只能对一维数组进行排序。
System.out.println(Arrays.toString(arr));//可以把数组元素拼接成一个好看的字符串,输出:[16, 13, 28, 56]

9.二维数组
  (1)数组是引用数据类型,所以里面可以再放数组,叫做二维数组。
  (2)定义:
int[][] arr;//推荐
int arr[][];
  (3)初始化
int[][] arr=new int[3][5];//3代表这个二维数组中有3个一维数组,每个一维数组里有5个元素
int[][] arr={{1,2,3,4,5},
                 {6,7,8,9,10},
                 {11,12,13,14,15}};
  (4)遍历二维数组
for(int i=0;i<arr.length;i++){//遍历的是二维数组里所有的一维数组
  for(int j=0;j<arr[i].length;j++){//遍历的是一维数组里的每一个元素
    System.out.print(arr[i][j]+" ");
  }
  System.out.println();
}

10.数组的缺点:数组的长度是固定的。以后工作中用集合。

面向对象
1.OOP的特点:封装encapsulation,继承inheritance,多态polymorphism
2.类的成员:成员变量、成员方法
  (1)成员变量:定义在类中、方法外的局部变量。
  (2)成员变量与局部变量的相同点和区别:
    相同点:成员变量与局部变量的定义是相同的。
    区别:
      A.在类中的位置不同。成员变量的位置在类中方法外。局部变量在方法内或者方法声明上(形式参数)。
      B.在内存中的位置不同。成员变量在堆内存。静态成员变量随着类的加载而加载,随着程序的结束而消失。局部变量在栈内存。
      C.生命周期不同。成员变量随着对象的创建而存在,随着对象的消失而消失。局部变量随着方法的调用而存在,随着方法的调用完毕而消失。
      D.初始化值不同。成员变量有默认值,可以不用初始化就可以说使用。局部变量没有默认值,必须先定义(即初始化)、赋值,最后使用。
  (3)成员方法:与方法的定义相同,区别是成员方法去掉了修饰符的static关键字。
3.类的使用:就是使用该类的成员(成员变量和成员方法)。
要想使用一个类的成员,就必须先拥有该类的对象,即创建对象。
  (1)格式:类名 对象名=new 类名();
  (2)对象访问成员:A.访问成员变量:对象名.成员变量
                            B.访问成员方法:对象名.成员方法(...)

封装
1.基本概念
  (1)把成员变量都用private修饰,对外提供public修饰的getXxx()、setXxx()方法。
  (2)封装的原则:A.将不需要对外提供的东东都隐藏起来。B.把属性隐藏,提供公共方法对其访问。
  (3)封装的好处:增加了代码的安全性和复用性。   弊端:牺牲了一些效率。  但是利大于弊,因为变量是不可控的,方法是可控的。
  (4)方法中成员变量访问的原则:就近原则
  (5)访问变量:局部变量.成员变量
2.this:代表所在类的对象引用,即代表当前的那个对象。谁调用的,this就代表谁。
  (1)如果局部变量名和成员变量名称一致,在方法中使用的时候采用的是就近原则,所以用this。
  (2)方法被那个对象调用,this就代表谁。
  (3)使用场景:就是用于解决成员变量被隐藏的问题。(用在局部变量隐藏成员变量的情况,即局部变量与成员变量重名)
3.构造方法Constructor:用于给对象的数据进行初始化
  (1)格式:
    A.方法名和类名相同
    B.没有返回值类型,连void都不能写
    C.没有具体的返回值
    D.构造方法可以重载
类名 对象名=new 构造方法名(...)
  (2)构造方法的注意事项
    A.如果我们没有给出构造方法,系统将给出一个默认的无参构造方法供我们使用。
    B.如果我们给出了构造方法,系统将不再提供构造方法供我们使用。
    C.构造方法也是可以重载的,一般的时候我们写构造方法写两个:空参构造方法、全参构造方法。
  (3)给成员变量赋值的方法
    A.setXxx()
    B.构造方法
  (4)构造方法的使用
    给属性初始化:构造方法是在创建对象的时候给属性进行初始化,只能用一次。
    用setXxx()方法进行初始化:当创建完对象后,再想修改属性值就需要用到setXxx()方法了。

继承
格式:public class 类名 extends 父类{}
1.构造方法不能继承。
2.开发的原则:高内聚,低耦合
高内聚:一个类或一个方法只完成一个功能,并且不依赖于其它的类或方法。就是自己完成某件事的能力。
低耦合:修改完一个方法后,其它的方法基本不需要动代码,维护起来很方便。类与类的关系尽量减少。
3.java中类的继承特点:(1)java中类只支持单继承,不支持多继承。(2)支持多层继承。
4.就近原则:成员变量名称相同时,在子类方法中访问变量,
  (1)在方法的局部范围找,如果有就使用。
  (2)在子类的成员范围找,如果有就使用。
  (3)在父类的成员范围找,如果有就使用。
  (4)如果还找不到,就报错。
5.this和super
  (1)this代表本类对象的引用。super代表父类的存储空间,也可以理解为代表父类对象的引用。
  (2)用法:
    A.访问成员变量:this.成员变量。super.成员变量
    B.访问构造方法:this(...)  调用本类的构造方法。super(...)  调用父类的构造方法
    C.访问成员方法:this.成员方法。super.成员方法
6.java继承中构造方法的访问特点
  (1)子类构造方法执行前,都会先执行父类的无参构造方法。因为子类会继承父类的成员方法,父类必须先初始化好了,子类才能继承。
  (2)在子类的构造方法中,默认第一行有一条语句super(),即必须在构造方法的第一行。
如果子类没有写super(),系统会默认加上一条。如果我们写了构造方法,系统就不会给我们提供构造方法了。
问题:如果父类中没有无参构造方法,怎么办呢?
回答:A.在父类中添加一个无参构造方法。(推荐)B.可以通过super去访问父类的带参构造方法。
我们在写构造方法的时候,最好写上无参构造方法和全参构造方法。
7.java继承中成员方法的访问特点:也是就近原则
  子类中方法和父类中方法的声明相同时(这叫方法的覆盖override),调用的是子类中的方法。
8.重载overload:在同一个类中出现的方法名相同的情况
  覆盖override:子类中出现了和父类中一模一样的方法声明的情况
9.方法覆盖的注意事项
  (1)@override注解  标记一个方法,表名该方法是覆盖父类的方法
  (2)父类私有的方法不能被覆盖
  (3)子类覆盖父类方法时,访问权限不能更低  private<默认修饰符friendly<protected<public
public:本类、别的包的类、本包的类
protected:本包中的类、其他包子类中
friendly:只能在本包中的类
private:只能在本类中
10.分析案例的时候是从子类分析到父类,写代码的时候是从父类写到子类。

多态
1.java多态的前提
  (1)有继承关系
  (2)有方法覆盖(不是必须的)
  (3)有父类引用指向子类对象  Fu f=new Zi();
Dog dog = new Dog();//我说这只狗是狗
Animal ani = new Dog();//我说这只狗是只小动物
2.多态中成员访问的特点
  (1)成员变量:编译看左边,运行看左边
  (2)成员方法:编译看左边,运行看右边
3.为什么成员变量和成员方法的访问不一样呢?
因为成员方法有覆盖,而成员变量没有覆盖。
4.多态的缺点:父类不能访问子类特有的方法。解决办法:向下转型。
多态的优点:提高了程序的扩展性。
具体体现:定义方法的时候,使用父类型作为参数,将来在使用的时候,使用具体的子类型参与操作。
fun(Animal a){...}
Cat c = new Cat();
调用:.fun(c);//等价于Animal a=new Cat();
5.多态中的转型问题
  (1)A.向上转型:从子到父,父类引用指向子类对象。Animal ani=new Dog();
     B.向下转型:从父到子,父类引用转为子类对象。
     注:强制转换可能会发生ClassCastException类型转换异常
  (2)在做向下转型之前,要先做判断
if(ani instanceof Dog){
  Dog dog=(Dog)ani;
  dog.lookDoor();
}
instanceof左边的对象应该是右边类的父类或同类对象。
6.静态成员变量:随着类的加载而加载,随着程序的消失而消失。以后会学集合、数组,假如集合、数组被static修饰了,如果你的这个集合或数组比较庞大,占用内存比较大,那么在程序结束之前,它始终会占据着内存空间。如果内存比较小的话,就会出现内存溢出异常。解决的办法:1.加内存;2.不要用static来修饰这种比较占内存的集合。

final和static关键字
1.final
  (1)final是一个关键字,表示最终的意思。可以用来修饰类、修饰变量、修饰方法。举例:绝育手术
  (2)final修饰类的时候:表明该类是最终类,不能被继承。
  (3)final修饰成员变量:表明该变量是常量,不能再次被赋值,只能赋值一次。
  (4)final修饰方法:表明该方法是最终方法,不能被覆盖override。
2.static
  (1)static是一个关键字,表示静态的意思。可以用来修饰成员变量和成员方法。
  (2)static修饰成员的特点:
    A.被类的所有对象共享。其实也是判断一个成员是否应该用static修饰的条件。
    B.可以通过类名直接访问。类名.static成员。推荐使用。
    C.优先于对象存在。优先于所有对象加载。
    D.随着类的加载而加载。(它在方法区中的静态区里)
3.使用场景及原因
package com.mystudy.constants;
public class Constants{
  /*放各种常量:地址、网址、硬盘上的地址*/
  public static final String ADDRESS = "中国北京";//调用:Constants.ADDRESS
  //static修饰:是为了用类名进行访问,用到常量的时候。
  //final修饰:不让外界进行修改
  //public修饰:可以在别的类中尽心访问
}
4.访问权限
  (1)非静态的成员方法:
    A.能访问静态的成员变量
    B.能访问非静态的成员变量
    C.能访问静态的成员方法
    D.能访问非静态的成员方法
  (2)静态的成员方法:
    A.能访问静态的成员变量
    B.能访问静态的成员方法
  (3)注意事项:静态成员方法中不能出现this、super这样的关键字。愿意你:静态是随着类的加载而加载,this、super这样的关键字是随着对象的创建而存在的。(开发的原则:先进内存的东东不能访问后进内存的东东。)

抽象类
1.在java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类。
2.抽象类、抽象方法都必须用abstract修饰。
public abstract void eat();
public abstract class Animal{}
3.抽象类的特点:
  (1)抽象类和抽象方法都必须用abstract来修饰。
  (2)抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类。
  (3)抽象类不能实例化,不能直接创建对象,但可以通过多态来创建对象。
抽象类如何实例化呢?参照多态的方式,通过子类实例化。
Animal ani = new Cat();
ani.ea();
ani.sleep();
  (4)抽象类的子类:要么重写抽象类中的所有抽象方法,要么也是抽象类。
4.抽象类的成员特点:
成员变量:有成员变量,成员变量可以是变量,也可以是常量。
构造方法:有构造方法。抽象类中构造方法的作用是用于子类访问父类数据的初始化。
成员方法:有成员方法,成员方法可以是抽象的,也可以是非抽象的。
  抽象方法:限定子类必须完成某些动作,起到一个规范和统一的作用。
  非抽象方法:提高代码的复用性。
5.思维的方式:
分析:从具体到抽象
实现:从抽象到具体
使用:使用的是具体的类的对象。
举例:老师、基础班老师、毕业班老师

接口
1.类或抽象类:放一些共性的东西,大家都有的东西,即“你会我也会,你有我也有。”
接口:定义的是额外的扩展性的东西,不是每个人都会的。
2.接口的特点:
  (1)定义接口使用的是interface关键字。
  (2)类和接口之间是实现关系,用implements关键字。class implements interface
  (3)接口不可以创建对象,可以使用多态的形式创建对象。它连构造方法都没有。
接口有没有其他的方式实例化呢?参照多态的形式使用类来实现实例化。
Jumping j = new Cat();
j.jump();
多态的几种形式:
A.具体类多态(几乎不用)
B.抽象类多态
C.接口多态(最常用)
  (4)接口的实现类   一个类实现一个接口。
要么重写接口中的所有抽象方法,要么这个类就是抽象类。
3.接口的成员特点:
成员变量:有成员变量,而且只能是常量。有默认的public static final修饰符,即是静态常量。
构造方法:没有构造方法
成员方法:有成员方法,而且都是抽象的。有默认的public abstract修饰符,即是抽象方法。
4.关系区别
类与类:继承关系。只能单继承,可以多层继承。
类与接口:实现关系。可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口。
接口与接口:继承关系,可以单继承,也可以多继承。
5.抽象类和接口的区别
  (1)成员区别:
抽象类:成员变量:可以是变量,也可以是常量。构造方法:有。成员方法:可以是抽象方法,也可以是非抽象方法。
接口:成员变量:只能是常量。成员方法:只能是抽象方法。
  (2)关系区别
类与类:继承关系。只能单继承,可以多层继承。
类与接口:实现关系。可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口。
接口与接口:继承关系,可以单继承,也可以多继承。
  (3)设计理念的区别
抽象类:被继承,体现的是is a。抽象类中定义的是继承体系的共性功能。
接口:被实现,体现的是like a。抽象中定义的是该体系的扩展功能。

四种权限修饰符
  1.本类  2.同一个包下(子类和无关类)  3.不同包下(子类和无关类)
                                                            3.1子类  3.2无关类 
private  1.Y
默认(friendly)  1.Y  2.Y
protected  1.Y  2.Y  3.1Y
public  1.Y  2.y  3.1Y  3.2Y

equals方法
1.功能:比较两个对象是否相等。如果没有重写的话,比较的是两个对象的地址值。重写后,可以比较两个对象的属性值。
2.==:
  (1)可以比较基本数据类型,比较基本数据类型的时候,比较的是基本数据类型的值是否相同。
  (2)可以比较引用数据类型,比较引用数据类型的时候,比较的是引用数据类型的地址是否相同。
3.如果想比较引用对象的内容是否相同,重写public boolean equals(Object obj){...}
4.s1.equals(s2);//this=s1,哪个对象调用的equals,this就是谁。obj=s2,被调用时的参数是哪个对象,obj就是谁。
5.重写equals方法
业务流程:
(1)地址值
(2)对象是否为空null
(3)是否是同一个类的对象
(4)obj向下转型,开始比较各个属性值。
(5)比较基本数据类型
(6)比较引用数据类型:a.对象是否为null。b.值。
具体代码:
public boolean equals(Object obj){
  //调用:cat.equals(cat2);
  //this: cat
  //obj: cat2
  if(this == obj) return true;//比较cat和cat2的地址是否相同。如果相同,说明它们是同一个对象,肯定相同。
  if(obj == null) return false;//看cat2是否为null。cat肯定不为null。如果cat为null,会报NullPointerException,根本走不到这个方法里。
  if(getClass() != obj.getClass()) return false;//看两个对象cat和cat2是不是同一个类的对象。如果不是同一个类的对象,说明不是同一个物种,直接return false,没有比较的必要。
  Cat other = (cat)obj;//把obj向下转型,为什么呢?因为下面要比较cat和cat2的属性值。如果不向下转型,还是obj的话,是不能获取cat2的属性值的。
  if(age != other.age) return false;//由于age属性是基本数据类型,可以直接用关系运算符比较。
  if(name == null){//看cat/this的name属性是否为null。这里为什么要判断name是否为null呢?如果name为null,两边比较name的属性值的时候,会报NullPointerException。
    if(other.name != null) return false;//如果this.name为null,但是cat2.name不为null,说明它们不相同,直接返回false。
  }else if(!name.equals(other.name)) return false;//比较this.name和cat2.name的值是否相同,如果不同,直接返回false。
  return true;
}//the end

String类
1.String:代表字符串类。
字符串是由多个字符组成的一串数组。
字符串的本质就是一个字符数组。
2.构造方法
String(String original):  把字符串数据封装成字符串对象。String s=new String("Hello");
String(char[] value): 把字符数组的数据封装成字符串对象。char[] chs={'H','E','L','L','O'};String s=new String(chs);
String(char[] value, int index, int count): 把字符数组的一部分数据封装成字符串对象。String s=new String(chs, 1, 2);
3.最常用的方式-直接赋值:String s="hello";
4.String类创建对象的特点
  (1)通过构造方法创建对象。通过构造方法创建的字符串对象是在堆内存中。
  (2)通过直接赋值的方式创建对象。通过直接赋值的方式创建的字符串对象,是在方法区的常量池中。(推荐用这个方式)
字符串的内容存储在方法区的常量池。这样做事为了方便字符串数据的复用。
String s1 = new String("hello");
String s2 = new String("hello");
String s3 = "hello";
String s4 = "hello";
System.out.println(s1 == s2);//false
System.out.println(s1 == s3);//false
System.out.println(s3 == s4);//true

代码
1.遍历字符串,即获取字符串中的每一个字符
String s = "16JKM13";
for(int i = 0; i < s.length(); i++){
  char ch = s.charAt(i);//获取字符串s中的某个索引位置的字符串元素
  if(ch >= '0' && ch <= '9'){//判断字符是不是数字
    System.out.println(ch);//比较的是ASCII码值
  }
}
//ch >='A' && ch <= 'Z'   大写字母
//ch >='a' && ch <= 'z'   大写字母
//ch -= ('a' - 'A');  从小写字母变成大写字母   'a'-'A'=32,  'A'=65,  'a'=97

2.字符串的拼接
int[] arr = {11,22,3,4,5};
String s = "[";
for(int i = 0; i < arr.length; i++){
  if(i == arr.length - 1){
    s += arr[i];
  }else{
    s += arr[i] + ", ";
  }
}
s += "]";
System.out.println(s);
//字符串拼接s+=...  因为String concat方法重载overload了

3.字符串反转  abc-->cba
String s = "abc";
String s_new = "";//空串
for(int i = s.length - 1; i >= 0; i--){
  char ch = s.charAt(i);
  s_new += ch;
}
System.out.println(s_new);

StringBuilder
1.StringBuilder:是一个可变的字符串类,内容是可变的,非常适合做字符串拼接操作。
String:内容是固定的,字符串都是常量。
2.构造方法
public StringBuilder();//创建一个空的字符串
public StringBuilder(String str);//把str这个字符串转成了StringBuilder对象
3.一些功能
  (1)添加功能:public StringBuilder append(任意类型);//添加数据,并返回对象本身
链式编程:sb.append("hello").append("world");//反着看,这是一个方法,方法由对象调用;正着看,对象调方法,返回的是一个对象。
  (2)反转功能:public StringBuilder reverse();
  (3)删除功能:public StringBuilder delete(int start, int end);//移除此序列的子字符串中的字符
4.StringBuilder和String的相互转换
StringBuilder-->String: public String toString();//通过toString()就可以把StringBuilder转换为String
String-->StringBuilder: public StringBuilder(String s);//通过构造方法就可以把String转换为StringBuilder
5.练习:字符串反转
思路:String-->StringBuilder-->reverse-->String
Scanner sc = new Scanner(System.in);
String s = sc.nextLine();
StringBuilder sb = new StringBuilder(s);
sb.reverse();
String s2 = sb.toString();

StringBuffer和StringBuilder:
StringBuilder:线程不安全,效率高。
StringBuffer:线程安全,效率低一些。
====

字符串的一些常用方法
----
字符串方法需要接收返回结果。
public char charAt(int index);//返回字符串中某个索引位置的字符元素
public boolean endWith(String small);//判断字符串是否已小字符结尾
public boolean equals(Object anObject);//判断两个字符串是否相等
public boolean equalsIgnoreCase(String anotherString);//判断两个字符串是否相等,忽略大小写
public int indexOf(int ch);//获取字符或小字符串在大字符串中的位置,不存在的话返回-1
public int length();//返回字符中元素的个数,字符串的长度
public String replace(CharSequence small, CharSequence replace);//把大字符串中的小字符串替换成replace,替换所有
例如:
  String s = "11, 22, 33, 44";
  String replace = s.replace(",", "-");
  System.out.println(replace);//11-22-33-44
public String replaceFirst(String small, String replace);//把大字符串中的小字符串替换成replace,只替换第一次出现的
例如:
  String s = "11,22,33,44";
  String replace = s.replaceFirst(",", "-");
  System.out.println(replace);//11-22,33,44
public String[] split(String small);//用小字符串切割大字符串
例如:
  String s = "11,22,33,44";
  String[] split = s.split(",");
  for(int i = 0; i < split.length; i++){
    System.out.println(split[i]);
  }
public boolean startsWith(String prefix);//判断大字符串是否以小字符串开头
public String subString(int beginIndex);//截取字符串,从索引beginIndex开始,截取到最后
例如:
  String s = "abcdefg";
  String subString = s.subString(3);
  System.out.println(subString);//defg
public String subString(int beginIndex, int endIndex);//截取字符串,从beginIndex开始,到endIndex结束,包含beginIndex,不包含endIndex
例如:
  String s = "abcedfg";
  String subString = s.subString(1, 4);
  System.out.println(subString);//bcd
public char[] toCharArray();//把字符串编程字符数组
例如:
  String s = "abcdefg";
  char[] chs = s.toCharArray();
  for(int i = 0; i < chs.length; i++){
    System.out.println(chs[i]);
  }
public String toLowerCase();//把字符串中的大写字母变成小写字母
例如:
  String s = "dfkdjALSJDLJF";
  s = s.toLowerCase();
  System.out.println(s);
public String toUpperCase();//把字符串中的小写字母变成大写字母
例如:
  String s = "dfkdjALSJDLJF";
  s = s.toUpperCase();
  System.out.println(s);
public static String valueOf(boolean b);//把各种类型的数据转成字符串
例如:
  String valueOf = String.valueOf(234);
  System.out.println(valueOf);//234
====

Arrays类
----
1.Arrays类提供了对数组操作的各种方法
2.常用方法
public static String toString(int[] arr);//把数组转换成字符串
public static void sort(int[] arr);//对数组进行升序排序
3.Arrays的构造方法是私有的。
私有构造方法是为了不让外界使用创建对象。
成员都用static修饰,可以用类名直接访问。
4.常用工具类的设计思想:(1)构造方法私有。(2)成员都用static修饰。
Math.pow(base, n);//base的n次幂     数学工具类
Collections:关系集合的一些操作方法
====

基本类型的包装类
----
1.为了对基本数据类型进行更多更方便的操作,java为每一种基本数据类型提供了一个对应的引用类型。
基本数据类型的包装类最常用的做法,就是做基本数据类型和字符串的相互转换。
基本类型  包装类
byte  Byte
short  Short
int  Integer
long  Long
float  Float
double  Double
char  Character
boolean  Boolean

String-->char:
String s = "a";
char c = s.charAt(0);

2.Integer类
构造方法:
Integer(int value);
Integer(String s);//s必须是数字字符串组成的,否则抛NumberFormatException
直接调用对象时,调用的是对象的toString()方法。toString()方法不重写,输出的是地址值。
把一个数字字符串转成一个包装类型,字符串里只能包含数字字符。

3.int与String类型的相互转换
Integer类的public int intValue();//Returns the value of this Integer as an int. 
Integer类的public static Integer valueOf(String s);//Returns an Integer object holding the value of the specified String. 
Integer类的public static Integer valueOf(int i);//Returns an Integer instance representing the specified int value. 
Integer类的public static String toString(int i);//Returns a String object representing the specified integer. 
Integer类的public static int parseInt(String s);//Parses the string argument as a signed decimal integer. 
String类的public static String valueOf(int i);//Returns the string representation of the int argument. 
int-->String:
  方法一:int number = 100;
               String s1 = "" + number;//比较方便,但不专业
  方法二:String s2 = String.valueOf(number);
String-->int:
  方法一:String-->Integer-->int
               String s = "100";
               Integer i = new Integer(s);
               int x = i.intValue();
  方法二:int y = Integer.parseInt(s);
总结:
int-->String: String::public static String valueOf(int i);
String-->int: Integer::public static int parseInt(String s);

4.练习题:把字符串排序"91 27 46 38 50"-->"27 38 46 50 91"
分析:
  (1)定义一个字符串对象。
  (2)把字符串中的数字数据存储到一个int型的数组中。
    A.字符串拆分成字符串数组String::public String[] split(String regex);
    B.字符串数组转成整型数组Integer::public static parseInt(String s);
  (3)对int数组进行排序Arrays::public static void sort(int[] arr);对指定的int型数组按数字升序进行排序
  (4)把排序后的数组中的元素进行拼接,得到一个字符串。
StringBuilder-->String:
StringBuilder.append(String s);
StringBuilder.toString();
代码:
import java.util.Arrays;
String s = "91 27 46 38 50";
String[] sArr = s.split(" ");
int[] numArr = new int[sArr.length];
for(int i = 0; i < numArr.length; i++){
  numArr[i] = Integer.parseInt(sArr[i]);
}
Arrays.sort(numArr);
StringBuilder sb = new StringBuilder();
for(int i = 0; i < numArr.length; i++){
  if(i ==numArr.length-1) sb.append(numArr[i]);
  else{sb.append(numArr[i]).append(" ");}
}
String s2 = sb.toString();
System.out.println(s2);

5.自动装箱和拆箱  JDK5提供的
  (1)自动装箱:把基本数据类型转换为对应的包装类类型。Integer::public static Integer valueOf(int i);//Returns an Integer instance representing the specified int value. 
  (2)自动拆箱:把包装类类型转换为对应的基本数据类型。Integer::public int intValue();//Returns the value of this Integer as an int. 
Integer i = 100;//自动装箱,这行代码等价于:Integer i = Integer.valueOf(100);
i +=200;//i = i + 200;  自动拆箱,然后自动装箱。这行代码等价于:i = Integer.valueOf(i.intValue() + 200);
Integer i3 = null;i3 += 300;//NullPointerException
在使用包装类类型的新特性的时候,如果做操作,最好先判断是否是null。
开发中的原则:只要是对象,在使用前就必须进行不为null的判断。
====

Date类和SimpleDateFormat
----
1.Date类:表示特定的瞬间,精确到毫秒
所属的包:import java.util.Date;
构造方法:
  Date();//根据当前时间创建的日期对象
  Date(long date);//从1970-01-01 00:00:00开始计算的时间,毫秒
long date = 1000(毫秒)*60(分钟)*60(小时),即1小时,因为是东八区,最后给出的是9点。用long类型是因为int类型会出现溢出。
long time = (long)1000*60*60*24*30,即一个月
Date d = new Date(time);//1970-01-31 08:00:00

2.Date对象<-->毫秒值
从Date对象得到一个毫秒值:public long getTime();//获取的是毫秒值,从1970-01-01 00:00:00开始
从一个毫秒值得到一个Date对象:方法一:public void setTime(long time);//设置时间,给的是毫秒值  方法二:构造方法Date(long time);

3.SimpleDateFormat
所属的包:java.text.SimpleDateFormat
功能:格式化和解析日期的具体类
格式化日期-->文本,Date-->String: public final String format(Date date);
解析文本-->日期,String-->Date: public Date parse(String source);//可能会有java.text.ParseException
代码:
  //Date-->String
  Date d = new Date();
  SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
  String s = sdf.format(d);
  System.out.println(s);//2019年07月10日 15:37:59
可以自己定义pattern,比如:
HH:mm:ss
yyyy-MM-dd HH:mm:ss EEE(EEE表示星期几)
因为Date对象在创立时,已经给每个字符设置了属性
  //String-->Date  把一个字符串解析为日期的时候,要注意模式字符串和给定的日期字符串的格式要匹配
  String str = "2080-08-08 12:23:45";
  SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  Date d = sdf.parse(str);//throws ParseException
  System.out.println(d);//Thu Aug 08 12:23:45 CST 2080

4.写Date工具类DateUtil
把日期转换为指定格式的字符串
把指定格式的字符串解析为日期
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DateUtil{
  private DateUtil(){}

public static String dateToString(Date date, String pattern){
    SimpleDateFormat sdf = new SimpleDateFormat(pattern);
    String s = sdf.format(date);
    return s;
  }

public static Date StringToDate(String data, String pattern) throws ParseException{
    SimpleDateFormat sdf = new SimpleDateFormat(pattern);
    Date d = sdf.parse(data);
    return d;
  }
}

5.Calendar日历类
所在包:java.util.Calendar
Calendar cal = Calendar.getInstance();//创建Calendar的对象,表示的是当前时间
int year = cal.get(Calendar.YEAR);
int month = cal.get(Calendar.MONTH) + 1;//第一个月是0,第二个月是1
int day = cal.get(Calendar.DAY_OF_MONTH);
int week = cal.get(Calendar.DAY_OF_WEEK) - 1;//星期日是1,星期一是2
cal.set(Calendar.YEAR, 2018);//设置年
int year2 = cal.get(Calendar.YEAR);//获取年
//Calendar的转换功能
Date date = new Date(1000);
cal.setTime(date);//可以把date表示的时间设置给cal, Date-->Calendar
Date time = cal.getTime();//用cal的获取时间的方法可以获取到date对象,Calendar-->Date
====

对象也可以比较大小
----
如果想比较两个对象的大小,可以通过实现Comparable接口,重写compareTo方法。
接口,比较器:java.lang.Comparable
方法:public int compareTo(T o);
调用方法:int i = stu1.compareTo(stu2);//this:stu1; o:stu2
如果大于,用1表示;
如果小于,用-1表示;
如果相等,用0表示。

类声明:
public class Student implements Comparable<Student>{
  @Override
  public int compareTo(Student o){
    if(this.getAge() > o.getAge()){return 1;}//大于
    else if(this.getAge() < o.getAge()){return -1;}//小于
    else{return 0;}//相等
  }
}
====

集合
----
一、集合的体系结构:见截图
二、Collection集合的基础知识
1.所在包:java.util.Collection
2.public interface Collection<E> extends Iterable<E>
3.Collection是单列集合的顶层接口。
它表示一组对象,这些对象也称为Collection的元素
JDK不提供此接口的任何直接实现。
创建Collection集合对象采用的是多态的方式。

4.<E>是一种特殊的数据类型,泛型
使用:在出现E的地方使用引用数据类型替换即可,如Collection<String>
import java.util.ArrayList;
Collection<String> c = new ArrayList<>();//JDK7的新特性
Collection<String> c = new ArrayList<String>();//多态形式,推荐
//boolean add(E e);添加元素
c.add("Hello");
c.add("World");

5.输出集合元素System.out.println(c);
ArrayList重写了toString()方法

6.集合中不能存放基本数据类型,如果想放基本数据,就用它对应的包装类
Collection<Integer> coll2 = new ArrayList<>();

三、Collection的成员方法
1.boolean add(E e);//添加元素
boolean add(int index, E e);//在指定位置添加元素
通过查看源代码,我们知道ArrayList集合的add方法返回值永远都是true。
2.boolean remove(Object o);//根据对象从集合中删除元素
boolean remove(int index);//根据索引从集合中删除元素
3.void clear();//清除集合中的元素
4.boolean contains(Object o);//判断集合中是否存在指定的元素
5.boolean isEmpty();//判断集合是否为空,空true,非空false
6.int size();//集合的长度,也就是集合中的元素个数

四、Collection集合的遍历
1.没有索引,不能用普通for
2.所在的包:java.util.Iterator
3.Iterator<E> iterator();//返回此Collection的元素上进行迭代的迭代器
Iterator<String> it = c.iterator();//返回的是迭代器接口的实现类的对象
通过集合对象调用iterator()方法,得到迭代器对象。
4.Iterator E next();//返回迭代的下一个对象,获取到下一个元素,并一道下一个,等待
NoSuchElementException:没有这样的元素异常
Iterator boolean hasNext();//如果仍有元素可以迭代,则返回true
while(it.hasNext()){
  String s = it.netxt();
  System.out.println(s);
}

5.代码
import java.util.Collection;
import java.util.ArrayList;
import java.util.Iterator;
Collection<String> c = new ArrayList<String>();//创建集合对象
c.add("小静");//给集合中添加字符串元素
c.add("大熊");
c.add("康夫");
Iterator<String> it = c.iterator();//通过集合对象获取到迭代器对象
while(it.hasNext()){//使用迭代器对象的方法获取数据
  String s = it.next();//在一次循环体中,只能调用一次it.next()
  System.out.println(s);
}

6.迭代器是集合遍历的一种方式,依赖于集合而存在。

五、集合的使用步骤
1.创建集合对象
2.创建元素对象
3.把元素添加到集合
4.遍历集合
  (1)通过集合对象获取迭代器对象
  (2)通过迭代器对象的hasNext()方法判断是否有元素
  (3)通过迭代器对象的next()方法获取元素,并指向下一个位置

====
List集合
----
1.所属包名:java.util.List
2.List有索引,可以用普通for循环。(没有索引,就不可以用普通for循环。)
3.List是有序的Collection,也称为序列。
4.List集合的特点:
  (1)有序(存储和取出元素的顺序一致)
  (2)存储的元素可重复

5.代码
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
List<String> list = new ArrayList<String>();
list.add("hello");
//...
Iterator<String> it = list.iterator();
while(it.hasNext()){
  String s = it.next();
  System.out.println(s);
}

6.List集合特有的成员方法(完全是针对索引操作的)
void add(int index, E element);//在指定位置添加元素,后面的元素往后移
E remove(int index);//删除指定位置的元素,返回被删除的元素
E get(int index);//获取指定位置的元素
E set(int index, E element);//修改指定位置的元素,返回被修改的元素

7.List集合的普通for遍历
  (1)迭代器
  (2)普通for循环 int size()
for(int i = 0; i < list.size(); i++){
  System.out.println(list.get(i));
}

8.List列表迭代器的特有功能
ListIterator<E> listIterator();//返回此列表元素的列表迭代器,允许程序员按任意方向遍历列表。正向遍历方式与Iterator相同。
public interface ListIterator<E> extends Iterator<E>;
ListIterator<String> list = list.listIterator();
特有功能:
E previous();//返回列表中的前一个元素
boolean hasPrevious();//如果以逆向遍历列表,列表迭代器有多个元素,则返回true
注意事项:ListIterator可以实现逆向遍历,但是要求先正向遍历,才能逆向遍历。

9.ConcurrentModificationException并发修改异常的原因及解决方案
产生的原因:迭代器依赖于集合而存在,在判断成功后,集合中添加了新元素,而迭代器并不知道,所以就报错了。即迭代器遍历集合中的元素的时候,不要使用集合对象去修改集合中的元素。
解决方案:
  方案一:用列表迭代器时,迭代器遍历的时候,用迭代器修改集合中的元素。元素是跟在刚才迭代的元素后面的。
  方案二:用普通for循环时,集合遍历的时候,可以通过集合对象修改集合中的元素。元素是在最后添加的。(推荐此方案)
方案一的代码:
ListIterator<Student> it = list.listIterator();
while(it.hasNext){
  Student stu = it.next();
  if(stu.equals(stu2)){
    it.add(stu4);//用迭代器添加
  }
}
方案二的代码:
for(int i = 0; i < list.size(); i++){
  Student stu = list.get(i);
  if(stu.equals(stu2)){
    list.add(stu4);//用集合添加
  }
}

10.正向删除时,要调整i,i--
逆向删除时,不用调整i
正向删除的代码:
for(int i = 0; i < list.size(); i++){
  if(list.get(i).equals("5")){
    list.remove(i);
    i--;
  }
}

逆向删除的代码:
for(int i = list.size(); i >= 0; i--){
  if(list.get(i).equals("5")){
    list.remove(i);
  }
}

11.增强for:是for循环的一种
格式:
for(元素的数据类型 变量名:数组名或者Collection集合对象名){
  使用变量名即可,这个变量名代表的就是数组或者Collection集合中的元素
}
例如:
for(int x:arr){
 System.out.println(x);
}
好处:简化了数组和Collection集合的遍历
弊端:目标不能为null
在遍历前,对目标做不为null的判断:
if(list != null){
  for(...:list){...}
}
增强for其实就是用来替代迭代器的。

用哪种循环?只想打印集合时,用增强for;如果有增删集合时,用普通for。
====

常见的数据结构
----
1.栈:先进后出。例如子弹夹
2.队列:先进先出。例如:排毒买票
3.数组:存储同一个数据类型的多个元素的容器,有索引,方便我们获取元素。特点:查询快,增删慢。

在数组中添加一个元素的步骤:
(1)定义一个新数组,长度是以前的数组的长度+1
(2)遍历以前的数组,得到每一个元素
(3)数前面的:按照以前的索引存储到新数组中。
   数本身:继续存储。
   数后面的:添加那个新元素,然后把索引+1存储到新数组中。

从数组中删除一个元素的步骤:
(1)定义一个新数组,长度是以前的数组的长度-1
(2)遍历以前的数组,得到每一个元素。
   数前面的:按照以前的索引存储到新数组中。
   数本身:不存储。
   数后面的:把索引-1存储到新数组中。

4.链表:由一个链子把多个节点连接起来的数据。
节点:由数据和地址组成。
特点:查询慢,增删快。
获取链表中的元素:从头开始

在链表中插入一个元素的步骤(最好自己画一个图):
(1)新元素应该也是一个节点
(2)把数本身的下一个元素地址用tmp存储。
(3)把新元素的地址付给数本身的下一个元素地址处
(4)把tmp的值赋给新元素的下一个元素地址处

从链表中删除一个元素的步骤(最好自己画一个图):
(1)把数本身的下一个元素地址用tmp存储
(2)把tmp的值赋给数本身的前一个元素的下一个元素地址处。

底层数据结构是数组结构,用ArrayList,查询快,增删慢。
底层数据结构是链表结构,用LinkedList,查询慢,增删快。
如果数据查询多,就选ArrayList。
如果数据增删多,就选LinkedList。
不知道时,选ArrayList,因为数据量少。

LinkedList的使用与ArrayList的相似。
====

集合的工具类Collections
----
所属包:java.util.Collections
ArrayList<Integer> list = new ArrayList<Integer>();
Collections.sort(list);//按正向排序
Collections.reverse(list);//反转
Collections.sort(list, Collections.reverseOrder());//倒序

Collection与Collections的区别:
Collection是单列集合的顶层接口。Collections是集合的工具类。
====

Set
----
Set:无序、唯一、元素不可重复,使用方式与Collection一致
在java.util包中,是一个接口,继承了Collection
1.不包含重复元素:HashSet不保证Set的迭代顺序,特别是它不保证该顺序恒久不变
2.什么时候使用?
  (1)要对List集合进行去重。
  (2)要求生成不重复的元素。
例子:在[1,10]生成10个不重复的数字。
代码:
import java.util.HashSet;
import java.util.Random;
HashSet<Integer> set = new HashSet<Integer>();
Random rd = new Random();
while(set.size() < 10){
  int num = rd.nextInt(10) + 1;
  //如果生成了相同的元素,这里是添加不了的。
  //只要添加不了,size是不会变的。
  set.add(num);
}
3.HashSet保证元素唯一性的原理:
通过查看add方法的源代码,我们知道了天价功能的执行过程中,是进行了数据的判断的,这个判断的流程是:
首先比较对象的哈希值是否相同。这个哈希值是根据对象的hashCode()计算出来的。
如果哈希值不同,就直接添加到集合中。
如果哈希值相同,继续执行equals()进行比较。返回的是true,说明元素重复,不添加。返回的是false,说明元素不重复,就添加。

如果我们使用HashSet集合存储对象,你要想保证元素的唯一性,就必须重写(override)hashCode()和equals()方法。注:是元素重写hashCode()和equals()方法。
代码:
import java.util.HashSet;
//创建集合对象
HashSet<String> hs = new HashSet<String>();
//添加元素
hs.add("Hello");//add调用的是Map.put()方法
hs.add("world");
//遍历集合
for(String s : hs){
  System.out.println(s);
}

Map::public V put(K key, V value){
  if(table == EMPTY_TABLE){
    inflateTable(threshold);
  }
  if(key == null) return putForNullKey(value);
  int hash = hash(key);
  int i = indexFor(hash, table.length);
  for(Entry<K, V> e = table[i]; e != null; e = e.next){
    Object k;
    if(e.hash == hash && ((k = e.key) == key || key.equals(k))){
      V oldValue = e.value;
      e.value = value;
      e.recordAccess(this);
      return oldValue;
    }
  }
  modCount++;
  addEntry(hash, key, value, i);
  return null;
}

4.因为我们存储的元素所属的类没有重写hashCode()和equals()方法,所以保证不了元素的唯一性,需要重写hashCode()和equals()方法。
@Override
public int hashCode(){
  final int prime = 31;
  int result = 1;
  result = prime * result + age;
  result = prime * result + ((name == null)? 0 : name.hashCode());
  return result;
}

@Override
public String toString(){
  return "Student [name=" + name + ", age="+ age +"]";
}

5.遍历:迭代器、增强for,不能用普通for,因为Set没有索引。

====

Map集合
----
Map集合中的实现类的数据结构只针对键有效。
1.public interface Map(K, V);//将键映射到值的对象。一个映射不能包含重复的键,每个键最多只能映射到一个值。
举例:学生的学号和姓名
Map<String, String> map = new HashMap<String, String>();//创建集合对象
map.put("it001", "林青霞");//添加元素, put(K key, V value);
键不能重复。如果键重复,就会用新值替换这个键的老值。
System.out.println(map);//输出结果

2.常用的成员方法
V put(K key, V value);//添加元素,如果键是第一次存储,就直接存储元素,返回null。如果键不是第一次存储,就用值把以前的值替换,返回以前的值。
V remove(Object key);//根据键删除键值对的元素,返回要删除的值
void clear();//移除所有的键值对元素
boolean containsKey(Object key);//判断集合是否包含指定的键
boolean containsValue(Object value);//判断集合是否包含指定的值
boolean isEmpty();//判断集合是否为空
int size();//返回键值对的对数

3.Map的获取方法
V get(Object key);//根据键获取值。没有键就返回null。
Set<K> keySet();//获取所有键的集合
Collection<V> values();//获取所有值的集合
代码:
map.get("张无忌");//返回赵敏
Set<String> set = map.keySet();//获取所有键的集合
for(String key : set){
  System.out.println(key);//郭靖,杨过,张无忌
}
Collection<String> values = map.values();//获取所有值的集合
for(String value : values){
  System.out.println(value);//黄蓉,小龙女,赵敏

4.Map集合的遍历
因为Map集合是双列的,所以不能用迭代器,不能用增强for。
遍历方式一:键找值
  思路:把Map看成是一个夫妻对的集合,A.把所有的丈夫给集中起来;B.遍历丈夫的集合,获取到每一个丈夫;C.根据丈夫去找对应的妻子。
  即:A.获取所有键的集合(keySet()); B.遍历键的集合,获取到每一个键(增强for);C.根据键去找值(V get(Object key))。
代码:
//创建集合对象
Map<String, String> map = new HashMap<String, String>();
//添加元素
map.put("郭靖", "黄蓉");
map.put("杨过", "小龙女");
map.put("张无忌", "赵敏");
//获取所有键的集合
Set<String> set = map.keySet();
//遍历键的集合,获取到每一个键
for(String key : set){
  //根据键去找值
  String value = map.get(key);
  System.out.println(key + "--" + value);
}

遍历方式二:键值对对象找键和值
  思路:A.获取所有结婚证的集合;B.遍历结婚证的集合,得到每一个结婚证;C.根据结婚证获取丈夫和妻子
  即:A.获取所有键值对对象的集合(Set<Map.Entry<K, V>> entrySet(););B.遍历键值对对象的集合,得到每一个键值对对象(增强for);C.根据键值对对象获取键和值(getKey(), getValue())。
代码:
//创建集合对象
Map<String, String> map = new HashMap<String, String>();
//添加元素
map.put("郭靖", "黄蓉");
map.put("杨过", "小龙女");
map.put("张无忌", "赵敏");
//获取所有键值对对象的集合Set<Map.Entry<K, V>> entrySet();
Set<Map.Entry<String, String>> set = map.entrySet();
//遍历键值对对象的集合,得到每一个键值对对象
for(Map.Entry<String, String> me : set){
  //根据键值对对象,获取键和值
  String key = me.getKey();
  String value = me.getValue();
  System.out.println(key + "--" + value);
}

练习题1.两种方式遍历
HashMap<String, Student>
键:String 学号
值:Student 学生对象
//创建集合对象
HashMap<String, Student> hm = new HashMap<String, Student>();
//创建元素对象
Student s1 = new Student("林青霞", 30);
Student s2 = new Student("张曼玉", 35);
Student s3 = new Student("王祖贤", 33);
//添加元素到集合中
hm.put("it001", s1);
hm.put("it002", s2);
hm.put("it003", s3);
//遍历
//遍历方式一:根据键找值
Set<String> set = hm.keySet();
for(String key : set){
  Student value = hm.get(key);
  System.out.println(key + "--" + value.getName() + "--" + value.getAge());
}
//遍历方式二:根据键值对对象找键和值
Set<Map.Entry<String, Student>> set2 = hm.entrySet();
for(Map.Entry<String, Student> me : set2){
  String key = me.getKey();
  Student value = me.getValue();
  System.out.println(key + "--" + value.getName() + "--" + value.getAge());
}

练习题2:任选一种方式遍历
HashMap<Student, String>
键:Student 学生对象
值:String 学生住址
要求:如果学生对象的成员变量值相同,就说明是同一个键
代码:
//创建集合对象
HashMap<Student, String> hm = new HashMap<>(Student, String);
//创建元素对象
Student s1 = new Student("林青霞", 30);
Student s2 = new Student("张曼玉", 35);
Student s3 = new Student("王祖贤", 33);
//添加元素到集合中
hm.put(s1, "北京");
hm.put(s2, "东京");
hm.put(s3, "纽约");
//遍历,键找值
Set<Student> set = hm.keySet();
for(Student key : set){
  String value = hm.get(key);
  System.out.println(key.getName() + "---" + key.getAge() + "---" + value);
}
在Student类中重写hashCode()和equals()方法,这样就保证了键的唯一性。

====
集合与集合的嵌套
----
1.ArrayList嵌套HashMap
题目1:需求:ArrayList集合嵌套HashMap集合,并遍历。
定义一个ArrayList集合,它包含三个元素,每一个元素都是HashMap类型的,每一个HashMap集合的键和值都是String类型的。
键:String 丈夫的姓名
值:String 妻子的姓名
数据:
第一个HashMap集合的元素:
  孙策  大乔
  周瑜  小乔
第二个HashMap集合的元素:
  郭靖  黄蓉
  杨过  小龙女
第三个HashMap集合的元素:
  令狐冲  任盈盈
  林平之  岳灵珊
代码:
//创建集合对象
ArrayList<HashMap<String, String>> array = new ArrayList<HashMap<String, String>>();
//创建元素1
HashMap<String, String> hm1 = new HashMap<String, String>();
hm1.put(" 孙策", "大乔");
hm1.put("周瑜", "小乔");
//把元素1添加到array中
array.add(hm1);
...//创建元素2、3,添加到array中
//遍历ArrayList集合
//遍历方法一:键找值
for(HashMap<String, String> hm : array){
  Set<String> set = hm.keySet();
  for(String key : set){
    String value = hm.get(key);
    System.out.println(key + "--" + value);
  }
  System.out.println("----");
}

题目2:对List进行去重
ArrayList<String> list = new ArrayList<String>();
list.add("小红");
...
HashSet<String> set = new HashSet<String>(list);//把ArrayList放到HashSet里,可以给元素去重
for(String s : set){
  System.out.println(s);
}
ArrayList<String> list2 = new ArrayList<String>(set);//再把HashSet转回ArrayList
for(String s : list2){
  System.out.println(s);
}

题目3:HashMap嵌套ArrayList
需求:HashMap集合嵌套ArrayList集合,并遍历
定义一个HashMap集合,它包含三个元素,每个元素的键是String类型,值是ArrayList类型。
键:String 人物来自哪部电视剧
值:ArrayList 人物的名称,每一个ArrayList集合的数据是String类型的
数据:
  第一个ArrayList集合的元素:三国演义
    诸葛亮
    赵云
  第二个ArrayList集合的元素:西游记
    唐僧
    孙悟空
  第三个ArrayList集合的元素:水浒传
    武松
    鲁智深
代码:
//创建集合对象
HashMap<String, ArrayList<String>> hm = new HashMap<String, ArrayList<String>>();
//创建元素1
ArrayList<String> arr1 = new ArrayList<String>();
arr1.add("诸葛亮");
arr1.add("赵云");
//把元素1添加到hm中
hm.put("三国演义", arr1);
...//创建元素2、3,添加到hm
//遍历集合
//遍历方法一
Set<String> set = hm.keySet();
for(String key : set){
  System.out.println(key);
  ArrayList<String> value = hm.get(key);
  for(String s : value){
    System.out.println("\t" + s);
  }
}

//遍历方法二:键值对对象找键和值
Set<Map.Entry<String, ArrayList<String>>> entrySet = map.entrySet();
for(Map.Entry<String, ArrayList<String>> entry : entrySet){
  System.out.println(entry.getKey());
  ArrayList<String> value = entry.getValue();
  for(String s : value){
    System.out.println("\t" + s);
  }
}

====
一些练习题
----
练习题1:统计字符串中每个字符出现的次数
String str = "...";
//HashMap<出现的字符, 字符出现的次数>
HashMap<Character, Integer> map = new HashMap<Character, Integer>();
//遍历字符串
for(int i = 0; i < str.length; i++){
  //取出一个字符
  char ch = str.charAt(i);
  if(map.containsKey(ch)){//如果包含ch,说明map统计过这个字符
    Integer count = map.get(ch);//把原来统计的次数拿出来
    map.put(ch, count+1);//把次数加1,再放回去
  }
  else{//如果map没有统计过ch的话
    map.put(ch, 1);
  }
}
Set<Map.Entry<Character, Integer>> entrySet = map.entrySet();
for(Map.Entry<Character, Integer> entry : entrySet){
  System.out.println(entry.getKey() + "(" + entry.getValue() + ")");
}

练习题2:删除集合中长度小于3的数据
练习题3:大小写转换
练习题4:集合反转排序
ArrayList<Integer> list = new ArrayList<Integer>();
Collections.reverse(list);
Collections.sort(list);
Collections.sort(list, Collections.reverseOrder());

练习题5:录入10个随机不重复的随机数
Random rd = new Random();
HashSet<Integer> set = new HashSet<Integer>();
while(set.size() < 10){
  int num = rd.nextInt(10);
  set.add(num);
}

练习题6:循环录入多个字符串,以end结束,排序。
Scanner sc = new Scanner(System.in);
ArrayList<String> list = new ArrayList<String>();
while(true){
  String s = sc.nextLine();
  if(s.equals("end")){
    break;
  }
  list.add(s);
}
Collections.sort(list);
System.out.println(list);

练习题7:给ArrayList去重
ArrayList<String> list = new ArrayList<String>();
list.add("小红");
list.add("小丽");
list.add("小红");
HashSet<String> set = new HashSet<String>(list);//用HashSet去重
for(String s : set){
  System.out.println(s);
}
ArrayList<String> list2 = new ArrayList<String>(set);
for(String s : list2){
  System.out.println(s);
}

练习题8:质数
int count = 0;
for(int i = 2; i < 100; i++){
  boolean flag = true;
  for(int j = 2; j < i; j++){
    if(i % j == 0){
      flag = false;
      break;
    }
  }
  if(flag){
    System.out.println(i + " ");
    count++;
    if(count % 5 == 0){
      System.out.println();
    }
  }
}

====
异常
----
异常就是程序出现了不正常的情况。
代码在编译或运行的时候出现的不正常的情况。
1.概念
  (1)Throwable类是java语言中所有错误(Error)或异常(Exception)的超类。
  (2)Error是Throwable的子类,用于指示合理的应用程序不应该试图补货的严重问题,如内存不足,即我们的程序不处理。
  (3)Exception类及其子类是Throwable的一种形式,它指出了合理的应用程序想要捕获的条件。也就是说针对程序发生了Exception的情况是我们需要处理的问题。在Exception类里有所有异常情况。
  (4)RuntimeException:是哪些可能发生在java虚拟机正常运行期间抛出的异常的超类。
  (5)Exception分为运行期异常和编译期异常。
    A.运行期的异常:在编译期是不处理的,在程序运行时出现了问题,需要我们回来修改代码。
    B.编译期的异常:在编译期就必须处理,否则程序不能通过编译,就更不能正常地执行了。

2.异常的体系
Throwable:是根类,不是接口。
|--Error:我们处理不了的,如硬件、内存不足等。严重问题,不需要程序处理。
|--Exception:我们可以处理的异常。
    |--运行时异常:代码在编译期不会报错,我们可以在出现问题后,修改代码,也可以想编译期异常一样,try...catch或throws。
    |--编译时异常:代码在编译期会报错,如果不对异常进行处理,程序没有办法执行,即必须先处理再运行。

3.JVM对异常的默认处理方式
java.lang.ArithmeticException:异常的类名,包括包名
by zero:异常的原因
at ... method(....java 17):异常的位置
如果程序出现了问题,我们没有做任何处理,最终JVM会做出默认的处理。处理方案:
  A.把异常的类名名称、异常的原因、异常出现的位置等信息在控制台输出。
  B.让程序停止执行。

4.异常处理方案try...catch
  (1)方案1:try...catch
格式:
try{
  可能出现异常的代码;
}catch(异常类名 对象名){
  异常的处理代码;
}

执行流程:程序从try开始执行,执行到哪里出现了问题,就会跳转到catch里面执行。执行完毕后,程序还能继续往下执行。
public void printStackTrace():把异常的错误信息输出到控制台。
在实际开发中,遇见异常会给出一个页面进行提示,我们暂时用printStackTrace来处理。

比较常用的格式:
try{
  可能出现异常的代码;
}catch(异常类名 对象名){
  异常的处理代码;
}
...
}catch(Exception e){//这一段catch代码要加载最后,用来接我们还未发现的异常
  System.out.println("各种异常");
  e.printStackTrace();
}

(2)方案2:throws
我们通过try...catch可以对异常进行处理了,但是并不是所有的时候,我们都有权限进行异常处理。也就是说,有些时候我们处理不了,但是这个时候异常是存在的,不处理也行,怎么办呢?
这个时候,java就提供了throws的处理方案。
格式:
throws 异常类名
注意这个格式必须跟在方法的括号的后面,在方法定义的后面
...method(...) throws Exception{...}
注意:编译时异常是必须要处理的,两种方案:try...catch或者throws。如果采用了throws这种方案,将来谁调用,还得进行处理。运行时异常可以不用处理,出现问题后,我们需要回来修改代码。

5.什么时候用try...catch,什么时候用throws?
正常情况下,出现异常要给用户一些提示,什么地方出现了问题。但是,不是每个方法里都可以和用户沟通。一般和用户沟通会有一个页面,但不是每个类都能弹出这个页面。
工具类的用throws,main或页面上的用try...catch。因为:工具类它只负责处理一些经常用的功能,它没有页面,不能和用户沟通,它的方法里如果出现异常,只能抛出throws。页面上、main这些可以和用户沟通的页面,可以对异常进行捕获try...catch,然后给用户一个友好的提示。
====

File类
----
所在包:java.io.File
File类是文件和目录路径名的抽象表示形式。也就是说,文件和目录是可以通过File封装成对象的。目录其实就是文件夹。
1.File构造方法
File(String pathname);//通过将给定  路径名  字符串  转换为  抽象路径名,来创建一个新File实例。
File(String parent, String child);//根据parent路径名  字符串和child路径名  字符串  创建一个新File实例。String parent必须是路径名
File(File parent, string child);//根据parent抽象路径名和child路径名  字符串创建一个File实例。
代码举例:把d:\\a\\b.txt转换为File对象
File f1 = new File("d:\\a\\b.txt");
File f2 = new File("d:\\a", "b.txt");
File f3 = new File("d:\\a"); File f4 = new File(f3, "b.txt");

2.File类的创建功能
public boolean createNewFile();//创建文件。如果文件不存在,创建文件并返回true;如果文件存在,创建文件失败,并返回false。
public boolean mkdir();//创建目录。如果目录不存在,创建目录并返回true;如果目录存在,创建目录失败,并返回false。
public boolean mkdirs();//创建多级目录
代码:
//在d盘目录下创建一个文件ee\f.txt
File f1 = new File("d:\\ee");
File f2 = new File("d:\\ee\\f.txt");
f1.mkdir();
f2.createNewFile();//throws IOException

3.File类的删除功能
public boolean delete();//删除文件和目录
File f1 = new File("a.txt");
f1.createNewFile();
注意:如果一个目录中有内容(目录或文件),就不能直接删除,要先删除目录或文件,再删除。即只能删除空目录。
绝对路径:是以盘符开始的路径。
相对路径:不以盘符开始,相对于当前的项目而言,在项目的目录下。

4.File类的判断和获取功能
判断功能:
public boolean isDirectory();//判断是否是目录
public boolean isFile();//判断是否是文件
public boolean exists();//判断是否存在
获取功能:
public String getAbsolutePath();//获取绝对路径
public String getPath();//获取相对路径
public String getName();//获取名称,文件全名+扩展名

5.代码示例:
File file = new File("aa/bb/cc/a.txt");
file.getParent();//返回文件的父路径,以字符串的形式返回
File parentFile = file.getParentFile();//返回文件的父路径,以文件的形式返回
parentFile.mkdirs();
file.createNewFile();
File dir = new File("aa/bb/cc");
File[] listFiles = dir.listFiles();//获取目录下所有的文件,以文件的形式返回
for(File file : listFiles){
  System.out.println(file.getName());
}
String[] list = dir.list();//获取目录下所有的文件,以字符串的形式返回
for(String s : list){
  System.out.println(s);
}

====
IO流
----
一、基本概念
IO流是用来处理设备Jan的数据传输问题。
数据流向:站在java程序的角度来看数据流向,从硬盘、文件读数据到java程序里,是输入流;从java程序里写数据到硬盘、文件,是输出流。
数据类型:分为字节流、字符流(字符流数据是通过notepad.txt打开后可以读懂的数据)
IO流常见的应用:文件复制、文件上传、文件下载
IO流分类:
  A.按照数据流向分:输入流、输出流
  B.按照操作类型分:字节流、字符流

二、字节流写数据
1.都是抽象基类
字节流:
  InputStream  字节输入流
  OutputStream  字节输出流(此抽象类是表示输出字节流的所有类的超类)
字符流:
  Reader  字符输入流
  Writer  字符输出流
字节流写数据outputStream,它的子类FileOutputStream文件输出流是用于将数据写入File

2.构造方法
FileOutputStream(String name);//创建一个向具有指定名称的文件中写入数据的输出文件流
//创建字节输出流对象,throws FileNotFoundException
FileOutputStream fos = new FileOutputStream("a.txt");//如果文件不存在,它会帮我们创建一个文件。如果文件存在,它会把文件内容清空。
创建字节输出流对象做了这样的三件事情:
  A.调用系统功能创建了a.txt文件
  B.调用写数据的方法
  C.释放资源
fos.write(65);//wite(int b)  写到文件里的是65,是文本阅读器把65转成了A
fos.write(66);
fos.close();//关闭此文件输出流,并释放与此留有关的所有系统资源

3.字节流写数据的步骤:
  A.创建字节输出流对象
  B.调用写数据的方法
  C.释放资源

4.构造方法
FileOutputStream(String name); throws FileNotFoundException
FileOutputStream(File file);
代码示例:
FileOutputStream fos = new FileOutputStream("b.txt");
FileOutputStream fos = new FileOutputStream(new File("b.txt"));
fos.write(65);

5.写字节 throws IOException
public void write(int b);//一次写一个字节
public void write(byte[] b, int off, int len);//一次写一个字节数组的一部分,off表示指定位置开始,len表示写几个字节
public void write(byte[] b);//一次写一个字节数组
代码示例:
byte[] bys = {65,66,67,68,69};
fos.write(bys);//ABCDE
//String类中的方法  String::public byte[] getBytes();把一个字符串转换为字符数组
fos.write("ABCDE".getBytes());
fos.write("ABCDE".getBytes(), 0, 3);//ABC

6.字节流写数据的常见问题
A.如何实现数据的换行?
不同的操作系统针对换行的符号识别是不一样的。
Windows: \r\n
Linux: \n
Mac: \r
加入换行符的代码示例:
FileOutputStream fos = new FileOutputStream("c.txt");
for(int i = 0; i < 10; i++){
  fos.write("hello".getBytes());
  //加入换行符号
  fos.write("\r\n".getBytes());
}
fos.close();

B.如何实现数据的追加写入?
用构造方法带第二个参数是true的情况即可
FileOutputStream(String name, boolean append);
如果第二个参数为true,则将字节写入文件末尾处,而不是写入文件开始处。

7.字节流写数据加入异常处理
方法一:分开做异常处理(太繁琐)
方法二:放在一起做异常处理。这种方式代码虽然简洁了,但是释放资源的动作可能未执行到
方法三:try-catch-finally
代码:
FileOutputStream fos = null;
try{
  fos = new FileOutputStream("d:\\d.txt");//如果操作系统没有d:这个盘符,就不会成功创建对象,fos就为null。
  fos.write("hello".getBytes());
}catch(IOException e){
  e.printStackTrace();
}finally{
  if(fos != null){
    try{
      //释放资源
      fos.close();//null调用方法,就会出异常
    }catch(IOException e){
      e.printStackTrace();
    }
  }
}

三、字节流读数据InputStream
FileInputStream  从文件系统中的某个文件中获得输入字节
1.构造方法
FileInputStream(String name);
代码示例:
FileInputStream fis = new FileInputStream("a.txt");
int by;
while((by = fis.read()) != -1){//读到文件的结尾,会返回-1
  System.out.println((char)by);//一次读取一个字节
}
fis.close();//释放资源

2.字节流读数据的步骤:
  A.创建字节输入流对象
  B.调用读数据的方法
  C.释放资源

3.字节流读数据
方式一:public int read();//一次读取一个字节,如果已经 到达文件末尾,则返回-1
方式二:public int read(byte[] b);//一次读取一个字节数组。从此输入流中将最多b.length个字节的数据读入一个byte数组中,返回值是读入缓冲区的字节总数,也就是实际的读取个数,如果已经到达文件末尾,则返回-1。
代码示例:
//String::public String(byte[] bytes);
//String::public String(byte[] bytes, int offset, int length);
byte[] bys = new byte[1024];//1024或1024的整数倍。因为1024byte=1M
int len;
while((len = fis.read(bys)) != -1){
  System.out.print(new String(bys, 0, len));//不用println(),因为文件里有换行符
}
fis.close();

4.IO字节流的练习题
练习题1:
题目描述:文件复制,其实就是从一个文件中读数据(数据源),然后把数据写到另一个文件中(目的地)。
数据源:d:\\窗里窗外.txt  -->读数据-->InputStream-->FileInputStream
目的地:林青霞.txt  -->写数据-->OutputStream-->FileOutputStream
代码:
//封装数据源
FileInputStream fis = new FileInputStream("d:\\窗里窗外.txt");
//封装目的地
FileOutputStream fos = new FileOutputStream("林青霞.txt");
//读写数据
//方式1:一次读取一个字节
int by;
while((by = fis.read()) != -1){
  fos.write(by);
}
//方式2:一次读取一个字节数组
byte[] bys = new byte[1024];
int len;
while((len = fis.read(bys)) != -1){
  fos.write(bys, 0, len);
}
//释放资源
fos.close();
fis.close();

练习题2:
题目描述:复制图片文件
数据源:d:\\mn.jpg  -->读数据-->FileInputStream
目的地:mn.jpg  -->写数据-->FileOutputStream
代码:
//封装数据源
FileInputStream fis = new FileInputStream("d:\\mn.jpg");
//封装目的地
FileOutputStream fos = new FileOutputStream("mn.jpg");
//读写数据
byte[] bys = new byte[1024];
int len;
while((len = fis.read(bys)) != -1){
  fos.write(bys, o, len);
}
//释放资源
fos.close();
fis.close();

四、字节缓冲流
字节流一次读写一个数组的速度比一次读写一个字节的速度快很多,这是加了数组这样的缓冲区效果。所以java提供了字节缓冲区流。
1.
BufferedOutputStream: 字节缓冲输出流
BufferedInputStream: 字节缓冲输入流

2.构造方法
BufferedOutputStream(OutputStream out);
使用这种构造方法,它提供了一个默认的缓冲区大小,所以一般我们使用默认的缓冲区就可以了。
为什么构造方法传递的是一个OutputStream,而不是具体的文件或路径呢?因为字节缓冲区流仅仅提供缓冲区,而真正的底层的读写数据还得需要基本的流对象进行操作。
代码示例:
FileOutputStream fos = new FileOutputStream("a.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos);
上述两行代码等价于:BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("a.txt"));
bos.close();//释放资源

3.写数据
bos.write(65);//写一个字节
bos.write("abcdefg".getBytes());//写一个字节数组
bos.write("abcdefg".getBytes(), 0, 4);//写一个字节数组的一部分

4.构造方法
BufferedInputStream(InputStream in);
代码示例:
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.txt"));
读取方式1:一次读取一个字节
int by;
while((by = bis.read()) != -1){
  System.out.print((char)by);
}
读取方式2:一次读取一个字节数组
int len;
byte[] bys = new byte[1024];
while((len = bis.read(bys)) != -1){
  System.out.print(new String(bys, 0, len));
}
//释放资源
bis.close();

5.字节流复制文件的效率
System类的public static long currentTimeMillis();//返回以毫秒为单位的当前时间
long start = System.currentTimeMillis();//记录开始时间
...//待测试效率的代码,如:methodA()
long end = System.currentTimeMillis();//记录结束时间
共耗时 = end - start;//毫秒
设计四个方案:
  方案A:基本字节流,一次读写一个字节
  方案B:基本字节流,一次读写一个字节数组
  方案C:缓冲字节流,一次读写一个字节(要求记住)
  方案D:缓冲字节流,一次读写一个字节数组(要求记住)
代码:
//方案A:基本字节流,一次读写一个字节
private static void methodA() throws IOException{
  //封装数据源
  FileInputStream fis = new FileInputStream("d:\\复制图片.avi");
  //封装目的地
  FileOutputStream fos = new FileOutputStream("copy.avi");
  int by;
  while((by = fis.read()) != -1){
    fos.write((char)by);
  }
  fos.close();
  fis.close();
}

//方案B:基本字节流,一次读写一个字节数组
private static void methodB() throws IOException{
  //封装数据源
  FileInputStream fis = new FileInputStream("d:\\复制图片.avi");
  //封装目的地
  FileOutputStream fos = new FileOutputStream("copy.avi");
  byte[] bys = new byte[1024];
  int len;
  while((len = fis.read(bys)) != -1){
    fos.write(bys, 0, len);
  }
  fos.close();
  fis.close();
}

//方案C:缓冲字节流,一次读写一个字节(要求记住)
private static void methodC() throws IOException{
  //封装数据源
  BufferedInputStream bis = new BufferedInputStream(new FileInputStream("d:\\复制图片.avi"));
  //封装目的地
  BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.avi"));
  int by;
  while((by = bis.read()) != -1){
    bos.write((char)by);
  }
  bos.close();
  bis.close();
}

//方案D:缓冲字节流,一次读写一个字节数组(要求记住)
private static void methodD() throws IOException{
  //封装数据源
  BufferedInputStream bis = new BufferedInputStream(new FileInputStream("d:\\复制图片.avi"));
  //封装目的地
  BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.avi"));
  byte[] bys = new byte[1024];
  int len;
  while((len = bis.read(bys)) != -1){
    bos.write(bys, 0, len);
  }
  bos.close();
  bis.close();
}
====

五、转换流
转换流=字节流+编码表
1.转换流出现的原因:因为字节流操作字符文件的时候,可能会出现乱码问题。
2.字节流读数据可能出现的问题:字节流一次读取一个字节的方式读取带有汉字的文件是有问题的。因为你读取到一个字节后,就转为字符在控制台输出了,而汉字是由2个字节组成的,所以这里会出问题。
3.文件复制的时候,字节流读取一个字节,写入一个字节,这个没有出现问题,是因为最终底层会根据字节做拼接,得到汉字。
4.汉字存储的规则:左边的字节数据肯定是负数,右边的字节数据可能是负数,也可能是正数,大部分情况下是负数。
代码示例:
String s1 = "hello";
String s2 = "你好";
byte[] bys1 = s1.getBytes();//[104,101,108,111]
byte[] bys2 = s2.getBytes();//[-60,-29,-70,-61]
System.out.println(Arrays.toString(bys1));
System.out.println(Arrays.toString(bys2));
创建一个输出流后,如果文件不存在,它会创建一个文件。如果文件存在,它会把文件清空。因为BufferedInputStream和BufferedOutputStream不能共用同一个文件。

六、编码表概述和常见的码表
1.编码表:由字符和对应的数据组成的一张表。
2.常见的编码表
ASCII:一个字节的7位表示数据,美国标准码表
ISO-8859-1:欧洲码表,一个字节的8位表示数据,兼容ASCII
GB2312:中文码表,兼容ASCII
GBK:中文码表的升级版,更多中文字符,兼容ASCII
UTF-8:可变长度的字符编码,用1-3个字节表示数据,又叫万国码表,兼容ASCII。用在网页上可以统一页面中的中文简体、繁体和其他语言的显示。

3.乱码问题:针对同一个数据,采用的编码和解码的编码不一致造成的。
为了不出现乱码的问题,要保证编码和解码采用统一的编码表。

七、字符串中的编码、解码问题
1.编码:
public byte[] getBytes(String charsetName) throws UnSupportedEncodingException;//使用指定的字符集将此String编码为byte序列,并将结果存储到一个新的byte数组中。默认编码为GBK。
String::public byte[] getBytes();//使用平台默认的编码表来编码
2.解码
String::public String(byte[] bytes, String charsetName);//通过使用指定的charset解码指定的byte数组,构造一个新的String
String::public String(byte[] bytes);//使用平台默认的编码表来解码
3.代码示例:
String s = "苍老师";
byte[] bys = s.getBytes("UTF-8");//编码
System.out.println(Arrays.toString(bys));
String s2 = new String(bys, "UTF-8");//解码
System.out.println(s2);

八、转换流的编码、解码
1.转换流其实就是一个字符流
转换流=字节流+编码表

2.字符输出流OutputStreamWriter: 
public OutputStreamWriter(OutputStream out);//根据默认编码把字节流转换为字符流
public OutputStreamWriter(OutputStream out, String charsetName);//根据指定编码,把字节流数据转换为字符流
代码示例:
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("osw.txt"));
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("osw.txt"), "UTF-8");
osw.write("你好");//调用写数据的方法
osw.close();//释放资源

3.OutputStreamWriter写数据的方法
public void write(int c);//写一个字符
public void write(char[] cbuf);//写一个字符数组
public void write(char[] cbuf, int offSet, int len);//写一个字符数组的一部分
public void write(String str);//写一个字符串
public void write(String str, int offSet, int len);//写一个字符串的一部分
写完数据后,没有发现数据,为什么呢?因为需要手动刷缓冲。
字符流写数据需要手动刷新缓冲。void flush()刷新该流的缓冲。close()方法在关闭流之前,先做了刷新缓冲的动作。
1字符=2字节
文件中的数据存储的基本单位是字节。
代码示例:
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("a.txt"), "UTF-8");
osw.write("我爱苍老师".toCharArray());//把字符串转成字符数组  String::char[] toCharArray() 将此字符串转换为一个新的字符数组
osw.write("我爱苍老师".toCharArray(), 0, 2);
osw.write("我爱苍老师");
osw.write("我爱苍老师", 0, 2);

4.字符输入流InputStreamReader
public InputStreamReader(InputStream in);//用默认的编码读数据
public InputStreamReader(InputStream in, String charsetName);//用指定的编码读数据
代码示例:
InputStreamReader isr = new InputStreamReader(new FileInputStream("osw.txt"), "UTF-8");
//读数据,一次读取一个字符数据
int ch;
while((ch = isr.read()) != -1){
  System.out.print((char)ch);
}
isr.close();//释放资源

5.InputStreamReader读数据的方法
public int read();//一次读取一个字符
public int read(char[] cbuf);//一次读取一个字符数组
代码示例:
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"), "UTF-8");
//方式1:一次读取一个字符
int by;
while((by = isr.read()) != -1){
  System.out.print((char)by);
}
//方式2:一次读取一个字符数组
char[] chs = new char[1024];
int len;
while((len = isr.read(chs)) != -1){
  System.out.print(new String(chs, 0, len));//String::public String(byte[] bytes, int offset, int length);
}
//释放资源
isr.close();

6.练习题:把当前项目目录下的StringDemo.java内容复制到当前项目目录下的copy.java中。
数据源:StringDemo.java  -->读数据-->字符流-->InputStreamReader
目的地:copy.java  -->写数据-->字符流-->OutputStreamWriter
//封装数据源
InputStreamReader isr = new InputStreamReader(new FileInputStream("StringDemo.java"));
//封装目的地
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("copy.java"));
//读写数据
//方式1:一次读写一个字符
int ch;
while((ch = isr.read()) != -1){
  osw.write(ch);
}
//方式2:一次读写一个字符数组
char[] chs = new char[1024];
int len;
while((len = isr.read(chs)) != -1){
  osw.write(chs, 0, len);
}
//释放资源
osw.close();
isr.close();

====
九、转换流的子类FileWriter, FileReader
转换流的名字比较长,而我们常见的操作都是按照本地默认编码实现的,所以为了简化书写,转换流提供了对应的子类。
1.FileWriter, FileReader
FileWriter extends OutputStreamWriter  用来写入字符文件的便捷类
FileReader extends InputStreamReader  用来读取字符文件的便捷类
OutputStreamWriter = FileOutputStream + 编码表
FileWriter = FileOutputStream + 编码表
InputStreamReader = FileInputStream + 编码表
FileReader = FileInputStream + 编码表

2.构造方法
FileWriter(String fileName);
FileReader(String fileName);
代码示例:
//封装数据源
FileReader fr = new FileReader("StringDemo.java");
//封装目的地
FileWriter  fw = new FileWriter("copy.java");
//读写数据
//方法1
int ch;
while((ch = fr.read()) != -1){
  fw.write(ch);
}
//方法2
char[] chs = new char[1024];
int len;
while((len = fr.read(chs)) != -1){
  fw.write(chs, 0, len);
}
//释放资源
fw.close();
fr.close();

十、字符缓冲区流BufferedWriter,BufferedReader
1.BufferedWriter:
将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够大了。
构造方法:
BufferedWriter(Writer out);//Creates a buffered character-output stream that uses a default-sized output buffer. 
BufferedWriter(Writer out, int sz);//Creates a new buffered character-output stream that uses an output buffer of the given size. 
写数据的方法:
void newLine();//Writes a line separator. 
void write(char[] cbuf, int off, int len);//Writes a portion of an array of characters. 
void write(int c);//Writes a single character. 
void write(String s, int off, int len);//Writes a portion of a String.

2.BufferedReader:
从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够大了。
构造方法:
BufferedReader(Reader in);//Creates a buffering character-input stream that uses a default-sized input buffer. 
BufferedReader(Reader in, int sz);//Creates a buffering character-input stream that uses an input buffer of the specified size. 
读数据的方法:
int read();//Reads a single character. 
int read(char[] cbuf, int off, int len);//Reads characters into a portion of an array. 
String readLine();//Reads a line of text.

3.代码示例:
//创建字符缓冲输出流对象
BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"));
//调用写数据的方法
bw.write("Hello");
//释放资源
bw.close();

//创建字符缓冲输入流对象
BufferedReader br = new BufferedReader(new FileReader("1.java"));
//方式1:一次读取一个字符
int ch;
while((ch = br.read()) != -1){
  System.out.print((char)ch);
}
//方式2:一次读取一个字符数组
char[] chs = new char[1024];
int len;
while((len = br.read(chs)) != -1){
  System.out.print(new String(chs, 0, len));
}
//释放资源
br.close();

4.练习题
题目描述:字符缓冲区流复制文件,把a.txt内容复制到b.txt中。
数据源:a.txt  -->读数据-->字符流-->InputStreamReader-->FileReader-->BufferedReader
目的地:b.txt  -->写数据-->字符流-->OutputStreamWriter-->FileWriter-->BufferedWriter
代码:
//封装数据源
BufferedReader br = new BufferedReader(new FileReader("a.txt"));
//封装目的地
BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));
//读写数据
//一次读写一个字符数组
char[] chs = new char[1024];
int len;
while((len = br.read(chs)) != -1){
  bw.write(chs, 0, len);
}
//释放资源
bw.close();
br.close();

5.特殊功能--换行功能
BufferedWriter::void newLine();//写入一个行分隔符,分隔符由系统属性决定。
BufferedReader::String readLine();//包含该行内容的字符串,不包含任何终止符。如果已到达流末尾,则返回null。即不读换行符。所以要自己换行System.out.println();
代码示例:
//创建字符缓冲输出流对象
BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt"));
//写数据
for(int i = 0; i < 3; i++){
  bw.write("Hello");
  //bw.write("\r\n");
  bw.newLine();
  bw.flush();
}
//释放资源
bw.close();

//创建字符缓冲输入流对象
BufferedReader br = new BufferedReader(new FileReader("bw.txt"));
String line;
while((line = br.readLine()) != null){
  System.out.println(line);
}
//释放资源
br.close();

6.练习题
题目描述:用字符缓冲区流的换行功能复制文件
数据源:a.java  -->BufferedReader
目的地:b.java  -->BufferedWriter
代码:
//封装数据源
BufferedReader br = new BufferedReader(new FileReader("a.txt"));
//封装目的地
BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));
String line;
while((line = br.readLine()) != null){
  bw.write(line);
  bw.newLine();
  bw.flush();
}
//释放资源
bw.close();
br.close();

十一、IO流总结(重点)
1.字节流
FileInputStream fis = new FileInputStream("a.txt");
FileOutputStream fos = new FileOutputStream("b.txt");

2.字节缓冲流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.txt"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("b.txt"));

3.转换流
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"), "UTF-8");
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("b.txt"), "UTF-8");

4.字符流
FileReader fr = new FileReader("1.java");
FileWriter fw = new FileWriter("2.java");

5.字符缓冲流
BufferedReader br = new BufferedReader(new FileReader("1.java"));
BufferedWriter bw = new BufferedWriter(new FileWriter("2.java"));

十二、IO流的一些练习题
练习题1:字符流复制文本文件
一共有5种方式:
  A.基本字符流一次读写一个字符串
  B.基本字符流一次读写一个字符数组
  C.缓冲字符流一次读写一个字符
  D.缓冲字符流一次读写一个字符数组
  E.缓冲字符串一次读写一个字符串
代码:
//A.基本字符流一次读写一个字符串
private static void methodA() throws IOException{
  FileReader fr = new FileReader("d:\\林青霞.txt");
  FileWriter fw = new FileWriter("窗里窗外.txt");
  int ch;
  while((ch = fr.read()) != -1){
    fw.write((char)ch);
  }
  fw.close();
  fr.close();
}

//B.基本字符流一次读写一个字符数组
private static void methodB() throws IOException{
  FileReader fr = new FileReader("d:\\林青霞.txt");
  FileWriter fw = new FileWriter("窗里窗外.txt");
  char[] chs = new char[1024];
  int len;
  while((len = fr.read(chs)) != -1){
    fw.write(chs, 0, len);
  }
  fw.close();
  fr.close();
}

//C.缓冲字符流一次读写一个字符
private static void methodC() throws IOException{
  BufferedReader br = new BufferedReader(new FileReader("d:\\林青霞.txt"));
  BufferedWriter bw = new BufferedWriter(new FileWriter("窗里窗外.txt"));
  int ch;
  while((ch = br.read()) != -1){
    bw.write((char)ch);
  }
  bw.close();
  br.close();
}

//D.缓冲字符流一次读写一个字符数组
private static void methodD() throws IOException{
  BufferedReader br = new BufferedReader(new FileReader("d:\\林青霞.txt"));
  BufferedWriter bw = new BufferedWriter(new FileWriter("窗里窗外.txt"));
  char[] chs = new char[1024];
  int len;
  while((len = br.read(chs)) != -1){
    bw.write(chs, 0, len);
  }
  bw.close();
  br.close();
}

//E.缓冲字符串一次读写一个字符串
private static void methodE() throws IOException{
  BufferedReader br = new BufferedReader(new FileReader("d:\\林青霞.txt"));
  BufferedWriter bw = new BufferedWriter(new FileWriter("窗里窗外.txt"));
  String line;
  while((line = br.readLine()) != null){
    bw.write(line);
    bw.writeLine();
    bw.flush();
  }
  bw.close();
  br.close();
}

====
练习题2:把ArrayList集合中的字符串数据存储到文本文件,每一个字符串元素作为文件中的一行数据
分析:
  A.创建集合对象
  B.往集合中添加字符串对象
  C.创建字符缓冲输出流对象
  D.遍历集合,得到每一个字符串元素,把字符串元素作为数据写到文本文件
  E.释放资源
代码:
//创建集合对象
ArrayList<String> array = new ArrayList<String>();
//往集合中添加字符串元素
array.add("hello");
array.add("world");
array.add("Java");
//创建字符缓冲输出流对象
BufferedWriter bw = new BufferedWriter(new FileWriter("array.txt"));
//遍历集合,得到每一个字符串元素,把字符串元素作为数据写入到文本文件
for(String s : array){
  bw.write(s);
  bw.newLine();
  bw.flush();
}
//释放资源
bw.close();

练习题3:从文本文件中读取数据到ArrayList集合中,并遍历集合,每一行数据作为一个字符串元素。
分析:
  A.创建字符缓冲输入流对象
  B.创建集合对象
  C.读取数据,每一次读取一行,并把该数据作为元素存储到集合中
  D.释放资源
  E.遍历集合
代码:
//创建字符缓冲输入流对象
BufferedReader br = new BufferedReader(new FileReader("array.txt"));
//创建集合对象
ArrayList<String> array = new ArrayList<String>();
//读取数据,每一次读取一行,并把该数据作为元素存储到集合中
String line;
while((line = br.readLine()) != null){
  array.add(line);
}
//释放资源
br.close();
//遍历集合
for(String s : array){
  System.out.println(s);
}

练习题4:把ArrayList集合中的学生数据存储到文本文件,每一个学生数据作为文件中的一行数据
分析:
  A.创建集合对象
  B.创建学生对象
  C.把学生对象添加到集合中
  D.创建字符缓冲输出流对象
  E.遍历集合,得到每一个学生对象,然后把该对象的数据拼接成一个指定格式的字符串,写到文本文件
  F.释放资源
代码:
//创建集合对象
ArrayList<Student> array = new ArrayList<Student>();
//创建学生对象
Student s1 = new Student("it001", "林青霞", 30, "北京");
Student s2 = new Student("it002", "张曼玉", 35, "东京");
Student s3 = new Student("it003", "王祖贤", 33, "纽约");
//把学生对象添加到集合中
array.add(s1);
array.add(s2);
array.add(s3);
//创建字符缓冲输出流对象
BufferedWriter bw = new BufferedWriter(new FileWriter("students.txt"));
//遍历集合,得到每一个学生对象
for(Student s : array){
  StringBuilder sb = new StringBuilder();
  sb.append(s.getSid()).append(", ").append(s.getName()).append(", ").append(s.getAge()).append(", ").append(s.getCity());//it001, 林青霞, 30, 北京
  bw.write(sb.toString());
  bw.newLine();
  bw.flush();
}
//释放资源
bw.close();

练习题5:从文本文件中读取学生数据到ArrayList集合中,并遍历集合,每一行数据作为一个学生元素。这里要使用String类中的方法split()。
分析:
  A.创建字符缓冲输入流对象
  B.创建集合对象
  C.读取数据,每一次读取一行数据,把该行数据想办法封装成学生对象,并把学生对象存储到集合中
  D.释放资源
  E.遍历集合
代码:
//创建字符缓冲输入流对象
BufferedReader br = new BufferedReader(new FileReader("students.txt"));
ArrayList<Student> array = new ArrayList<Student>();
//读取数据,每一次读取一行数据
String line;
while((line = br.readLine()) != null){it001, 林青霞, 30, 北京
  String[] strArray = line.split(", ");
  Student s = new Student();
  s.setSid(strArray[0]);
  s.setName(strArray[1]);
  s.setAge(Integer.parseInt(strArray[2]));
  s.setCity(strArray[3]);
  array.add(s);
}
//释放资源
br.close();
//遍历集合
for(Student s : array){
  System.out.println(s.getSid() + ", " + s.getName() + ", " + s.getAge() + ", " + s.getCity());
}

====

java学习笔记:全部,txt版本相关推荐

  1. JAVA学习笔记系列4-Eclipse版本选择

    下载Eclipse需要根据安装的JDK的版本来决定是安装32位还是64位,不是根据操作系统选的. 转载于:https://www.cnblogs.com/feiyafei/p/9935274.html

  2. 《Java学习笔记(第8版)》学习指导

    <Java学习笔记(第8版)>学习指导 目录 图书简况 学习指导 第一章 Java平台概论 第二章 从JDK到IDE 第三章 基础语法 第四章 认识对象 第五章 对象封装 第六章 继承与多 ...

  3. Java 学习笔记:第一章 Java入门

    Java 学习笔记:第一章 Java入门 1.1 计算机语言发展史以及未来方向 1.2 常见编程语言介绍 C语言 C++ 语言 Java语言 PHP 语言 Object-C和Swift 语言 Java ...

  4. java学习笔记---5

    IO流 I〇流概述: lO:输入/输出(Input/Output) 流:是一种抽象概念,是对数据传输的总称.也就是说数据在设备间的传输称为流,流的本质是数据传输IO流就是用来处理设备间数据传输问题的: ...

  5. JAVA学习笔记(1)【基础知识】

    JAVA学习笔记DAY_1 提示:关于java系列的内容只是本人在老师的指导下和自学过程中的一些学习笔记,如果存在错误敬请批评指正! 文章目录 JAVA学习笔记DAY_1 前言 一.Java语言未来的 ...

  6. Java学习笔记(原创)

    Java学习笔记(原创) 2011-12-01 16:37:00|  分类: Java|举报|字号 订阅 下载LOFTER客户端 基本知识 一. Java基础 1. java语言的特点: ①简单:没有 ...

  7. java学习笔记12--异常处理

    java学习笔记系列: java学习笔记11--集合总结 java学习笔记10--泛型总结 java学习笔记9--内部类总结 java学习笔记8--接口总结 java学习笔记7--抽象类与抽象方法 j ...

  8. java学习笔记16--I/O流和文件

    本文地址:http://www.cnblogs.com/archimedes/p/java-study-note16.html,转载请注明源地址. IO(Input  Output)流 IO流用来处理 ...

  9. java学习笔记7--抽象类与抽象方法

    接着前面的学习: java学习笔记6--类的继承.Object类 java学习笔记5--类的方法 java学习笔记4--类与对象的基本概念(2) java学习笔记3--类与对象的基本概念(1) jav ...

最新文章

  1. CV算法复现(分类算法4/6):GoogLeNet(2014年 谷歌)
  2. javafx 使用_何时使用JavaFX代替HTML
  3. 大型网站技术架构:核心原理与案例分析 mobi_大数据技术经典学习路线
  4. android 滚动条自定义样式,IScroll的使用-方向键绑定自定义滚动条样式
  5. 一文带你纵览 200+ 大规模机器学习研究
  6. sap模块有哪些_SAP那些事-实战篇-21-关于COPA模块的总结
  7. 使用Jquery+CSS如何创建流动导航菜单-Fluid Navigation
  8. iOS直播集成和问题总结(阿里云直播)
  9. Linux ubuntu 串口调试工具
  10. 使用GSON解析JSON数据
  11. lib、dll文件默认打开方式改变之后进行恢复
  12. 使用Python解密仿射密码
  13. 怎么做硬件产品的需求分析?
  14. 使用ping命令检测设备在线
  15. 论文笔记32 -- Conformer: Local Features Coupling Global Representations for Visual Recognition
  16. 从事文字工作和经常使用电脑的人要注意保护好自己的眼睛
  17. vue-seamless-scroll表格无缝滚动
  18. Uni-app APP开发、适配指北
  19. ZOJ3380_Patchouli's Spell Cards_概率DP
  20. sql server基础语句大全

热门文章

  1. 多行溢出隐藏显示省略号功能的JS实现
  2. 用HTML+CSS编写一个计科院网站首页的静态网页
  3. 学python先学什么基础_python学完基础学什么
  4. linux 查看公网ip
  5. python程序停止运行语句_怎么停止python脚本
  6. PCBA产业摩尔MES解决方案
  7. 单片机原理与应用技术课后答案(3)
  8. java反射一篇搞定
  9. [宋史学习] 赵光义对西夏的一系列错误
  10. RISC架构与CISC架构对比