JavaEE 基本知识

构造方法

作用:

用于给对象中的成员变量赋值。在创建对象的同时,会自动调用构造方法,等对象创建完成的时候,对象中的成员变量就已经有指定的值了。

构造方法的定义格式:

   修饰符 方法名称(参数列表) {

       方法体

}

构造方法格式的说明:

1、构造方法的方法名称,必须和类名一模一样,连大小写都一样。

2、构造方法没有返回值类型,连void也没有

3、构造方法没有return语句,如果一定需要return语句,就写一个return;

构造方法其他说明:

1、构造方法不需要手动调用,由jvm虚拟机在创建对象的时候自动调用

2、对象本身不能调用构造方法

3、构造方法只调用一次。

4、构造方法不能通过对象名称手动调用   对象名.方法名();

5、如果在类中没有定义任何的构造方法,那么系统会自动提供一个空参构造(空实现)

6、如果在类中手动定义了一个构造方法(无论是空参还是有参),系统都不再会提供任何的构造方法。

构造方法的重载:

在同一个类中,方法名相同,参数列表不同,与返回值类型无关

构造方法都在同一个类中,构造方法的方法名称都和类名一致,【参数列表不同】,返回值类型没有

   一般在类中,既需要空参构造,也需要有参构造,都手动定义出来

构造方法和set方法的比较:

构造方法和set方法都是用于给成员变量赋值。不希望外界直接访问私有成员变量,通过构造方法或者set方法,间接的访问私有变量。

区别:

   构造方法是在创建对象的同时,由jvm自动调用执行,用于给属性赋值,只能执行一次

   set方法是在创建对象之后,由对象手动调用执行,用于给属性修改值,可以调用多次

使用场景比较:

   一般set方法使用更加灵活,使用更加频繁

   构造方法,只能在创建对象的时候被自动调用一次,代码更加简洁。一旦对象创建成功,就不能继续使用构造方法修改成员变量的值。

创建对象内存理解

创建对象的过程中,有三个初始化的步骤:

构造方法初始化

默认的初始化

显式初始化

三者的顺序:

通过使用空参构造,发现打印内容是显式初始化结果,说明默认初始化在显式初始化之前;

通过使用有参构造,发现打印内容是构造方法初始化结果,说明显式初始化在构造方法初始化之前;

顺序:默认初始化、显式初始化、构造方法初始化

创建对象的过程:

   1、将创建的对象所属类型加载到方法区

   2、在栈内存中创建对象的引用,将来用于存储对象的地址

   3、在堆内存中开辟内存空间,给成员变量分配内存

   4、给成员变量默认初始化赋值

   5、给成员变量显式初始化赋值

   6、给成员变量构造方法初始化赋值

   7、将对象的地址,赋值给栈内存中的引用

静态

没有静态:

如果某个类型的所有对象,都具有一个相同的属性值,那么这个属性值就没有必要在所有对象中,都存储一份。还有坏处:浪费内存空间;维护难度大,一旦需要修改,就得修改所有的对象。

有静态的状态:

如果某个类型的所有对象,都具有一个相同的属性值,那么就在这个属性的定义上,加一个static静态关键字。让该变量存储在方法区字节码的静态区中,避免了所有对象都存储相同数据的问题,节省了内存空间,将来维护容易(只需要修改一次)

静态特点:

静态的解释:static关键字

   静态、静止的。静态变量不会随着对象的变化而变化

加载时机:

   随着类的加载而加载。

静态变量随着类的加载进方法区,就直接在静态区给开辟了存储静态变量的内存空间

静态变量优先于对象而存在

静态变量被所有该类对象所共享

代码层面:可以使用类名直接调用,不需要使用对象名称。在不创建对象的前提下,仍然可以使用这个静态变量。建议使用类名来访问。

静态注意事项:

静态方法:在方法声明上,加上了static关键字的方法,就是静态方法

静态方法不能访问非静态的变量

原因:静态方法可以在没有创建对象的时候调用;非静态的变量只有在对象创建之后才存在。如果静态方法可以访问非静态的变量,那么就相当于在对象创建之前,就访问了对象创建之后的数据。明显不合理。

静态方法不能访问非静态的方法

原因:静态方法可以在没有创建对象的时候调用;非静态的方法可以访问非静态的变量。如果静态方法可以访问非静态的方法,就相当于静态方法间接的访问了非静态的变量,和第2点矛盾。

静态方法中不能存在this关键字

原因:this关键字表示本类当前对象。静态方法可以在对象创建之前调用。如果静态方法可以访问this关键字,相当于在创建对象之前,就使用了对象本身。矛盾。

总结:静态不能访问非静态

静态变量和非静态变量的区别

概念上,所属不同:

   非静态变量属于对象

   静态变量属于类,类变量

内存空间不同,存储位置不同

   非静态变量属于对象,所以存储在堆内存中

   静态变量属于类,存储在方法区的静态区中

内存时间不同,生命周期不同

   非静态变量属于对象,所以生命周期和对象相同,随着对象的创建而存在,随着对象的消失而消失

   静态变量属于类,所以生命周期和类相同,随着类的加载(创建对象、类名访问静态变量、类名访问静态方法、反射的方式、加载子类、运行某个测试类)而存在,随着类的消失(内存管理)而消失

访问方式不同

   非静态变量只能使用对象名访问

   静态变量既可以使用对象访问,也可以通过类名访问:

       类名.静态变量名

       类名.静态方法名()

主方法解释

主方法:

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

关键字public

   公共状态,权限修饰符,所有情况都可以访问

   主方法是jvm虚拟机直接调用的,语言之间相互调用,需要使用最大的最高的权限

关键字static

   静态状态,在不创建对象的前提下也可以访问

   主方法是jvm虚拟机直接调用的,测试类中,不需要定义成员变量,也不需要有对象存储数据,可以直接调用主方法,主方法没有必要声明为非静态;jvm是C语言写的,是一个面向过程的语言,不能创建对象。

关键字void

   空,返回值类型为空

   主要使用java语言操作各种数据,如果主方法有返回值,返回给调用者(jvm虚拟机),返回给jvm虚拟机之后,我们没有办法操作这个数据了,没有任何意义。

main方法:

   主要的,不是一个关键字

   在jvm虚拟机中,要执行某个类的时候,只执行main方法,在jvm的代码中,已经将要访问的方法,程序的唯一入口,写死了

String[] args形式参数

   字符串类型的数组

   在执行主方法的时候,可以传入一些参数,将来在主方法中,可以接收到这些参数

   Args是一个参数名称,任意修改

   String[]的中括号也可以写到args后面

工具类的使用

Math.random();范围是0.000~0.999

equals(int[] arr1, int[]arr2)比较arr1和arr2数组中的元素是否全部相等

fill(int[] arr, int val)使用val值,将arr数组填充满

sort(int[] arr)将arr数组进行升序排序

toString(int[] arr)将arr数组中的数据,都以字符串的形式显示出来

帮助文档制作:

   会写文档注释/**文档注释*/

   @author、@version、@param @return

多态

多态:

事物的多种状态,polymorphic

对象的多态性:同一个对象,可能具有不同的名称,同一个对象,有不同的类型的引用指向它。本质:同一个物体有不同的名称和描述。

类型的多态性:同一个类型,可能具有不同的子类实现。同一个类型引用,有不同的对象实现。本质:同一个名字和描述,可以在不同场景下有不同的真实实现。

父类的声明指向子类的实现,在调用的时候,会调用子类的方法。

多态的前提:

   1、要有子父类的继承(实现)关系

   2、有方法的重写,没有方法的重写就是一个没有意义的重写

   3、必须是父类(或父接口)引用指向子类对象(或接口实现类)【格式体现】

多态中成员变量的访问特点:

编译看左边,运行看左边

编译的时候,要看【=】左边(父类)的引用的类型中,是否有该变量的定义,如果有,就编译成功,如果没有,就编译失败。

运行的时候,要看【=】左边的引用所属类型中,真正如何给变量赋值的。获取到的是引用父类的赋值结果(如果没有赋值,则是变量的初始值)。

    如果想调用子类相同名字的成员变量值,则需要向下转型:

   Animal a = new Cat();

   Cat b = (Cat) a;

//将Animal引用数据a 向下强制转换为cat的引用数据类型b

   b.变量名  方式来获取!

多态中成员方法的访问特点:

编译看左边,运行看右边

编译的时候,要看【=】左边的引用所属的类型中,是否有该方法的定义,如果有,就编译成功,如果没有,就编译失败。

运行的时候,要看【=】右边的对象所属的类型中,是如何实现这个方法的。最终运行的是子类重写过的方法实现。

    如果想调用子类独有的方法,则需向下转型:

   Animal a = new Cat();

   Cat b = (Cat) a;

   b.独有方法名() 来获取子类独有的方法。

如果想调用父类和子类重名的方法,则需要重新创建父类对象(非多态)

    Animal a = new Animal();

向上转型:

父类类型 引用名称 = new 子类的对象

Fulei name1 = new Zilei();

  向下转型:

     子类类型 引用名称 = (子类类型)父类类型的引用

     Zilei name2  =  (Zilei)name1

多态中静态方法的访问特点

编译看左边,运行看左边

编译的时候,要看【=】左边的引用所属的类型中,是否有该方法的定义,如果有,就编译成功,如果没有,就编译失败

运行的时候,要看【=】左边的引用所属的类型中,如何实现该方法的。最终运行的是引用所属类型中该方法的实现。(静态方法属于类,和父类的方法不属于重写关系)

静态最终解释:

   静态变量:存储在类的字节码中的变量,被所有对象所共享,不随着对象的变化而变化,都有相同的值,所以称为静态变量

   静态方法:只会根据引用所属的父类,来决定运行的内容,运行内容,不会随着子类的变化而变化,都是引用所属的父类的方法实现,所以称为静态方法。

引用类型转换

转型的意义:

当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点"小麻烦"。所以,想要调用子类特有的方法,必须做向下转型。

向上转型:

多态本身是子类类型向父类类型向上转换的过程,这个过程是默认的。

当父类引用指向一个子类对象时,便是向上转型即new出来的是子类,但是却声明为了父类。

父类类型 变量名 = new 子类类型();

如:Animal a = new Cat();

向下转型:

父类类型向子类类型向下转换的过程,这个过程是强制的。

一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。

子类类型 变量名 = (子类类型) 父类变量名;  如:Cat c =(Cat) a;

转型异常:

变量名 instanceof 数据类型

如果变量属于该数据类型,返回true。

如果变量不属于该数据类型,返回false。

例子:if (a instanceof Cat)

抽象类、接口的选用

1.在实际开发中接口和抽象类一般都用来表示规范,但是优先选用接口.(因为接口可以多实现)

2.如果还需要定义普通方法和成员变量才考虑使用抽象类

3.抽象类在实际中还经常用来定义模版方法

由普通的成员变量变成了类变量,1,节省了内存 2,不通过对象就能调用,直接通过类名.的方式直接调用,不依赖于对象,不取决于对象

new 对象,对象里面有成员变量和成员方法,1 属性赋值,为了用对象来传递数据 2 为了调用或者执行它的成员方法

关键词Static:静态修饰符,他可以修饰成员变量和成员方法,意味着该变量是静态变量(类变量)该类的所有对象都能访问修改。方便在没有创建对象的情况下来进行调用(方法/变量)。

把static加载成员方法前边:有些情景下,我们不需要传递数据,只想直接调用方法,因此我们想了一个办法,在普通的成员方法前边,加上static关键字,因此这个方法就变成了 类级别的方法,不依赖于对象,直接通过类名.方法名(参数)来调用

static 加载代码块的前边,我们称之为静态代码块,目的:给静态变量来初始化值

调用的注意事项:

静态的只能调用静态的,非静态的没有限制

静态方法中调用非静态变量,可以通过创建类的对象,然后通过对象来访问非静态变量

静态方法里面不能够使用this,因此this所指代的是某个对象,哪个对象调用该方法,就指代的是哪个对象,但是我们静态方法是直接通过类名调用的,没有用到对象,所以,this不能够指代哪个对象,所以在静态方法里面不能够使用this

被static修饰的成员变量变成类变量(静态变量),

调用的时候格式是:类名.静态变量名

是随着类的加载而加载的,且只加载一次。

存储于一块固定的内存区域(静态区),所以,可以直接被类名调用。

它优先于对象存在(对象还没存在时,它就已经存在了),所以,可以被所有对象共享。

抽象类

抽象方法:

抽象:抽取像的、相同的、相似的内容出来

抽象方法:只有方法声明,而没有方法实现的方法,就是抽象方法

   在各个子类中,对于某个方法都有自己不同的实现,所以实现各不相同,无法抽取,只能抽取方法的声明上来,在父类中,方法就只有方法声明没有方法体。就是抽象方法。

定义格式:

   1、没有方法实现,连大括号都没有,直接在方法声明后面加上一个分号,表示方法定义结束

   2、为了将方法标记为抽象方法,需要在方法前面加上一个abstract关键字

抽象类:

抽象类和抽象方法都需要使用abstract关键字修饰

   抽象类:abstract class {}

   抽象方法:public abstract void test();

抽象类和抽象方法的关系:

   抽象方法所在的类必须是抽象类

   抽象类中未必一定都定义抽象方法,抽象类中未必存在抽象方法

抽象类的实例化(抽象类如何创建对象)

   抽象类不能直接实例化

   定义抽象类的子类,由子类创建对象,调用方法

抽象类子类的前途

   在子类中,将父类所有的抽象方法全部重写(实现),子类就成了一个普通类,就可以创建对象

   在子类中,没有将父类中所有的抽象方法全部实现,子类就还是一个抽象类,还需要使用abstract关键字修饰子类。

抽象类的特点:

成员变量:可以定义变量,也可以定义常量,但是不能被抽象

构造方法:有

   虽然本类无法创建对象,但是抽象类一定有子类,子类会访问父类的抽象方法。

   是否有构造方法,不取决于是否可以创建对象,而是取决于是否可以定义成员变量。如果可以定义成员变量,那么就需要初始化成员变量,就是构造方法来完成的。

成员方法:

   既可以是抽象方法:强制子类重写

也可以是非抽象方法:用于给子类继承,提高代码的复用性

代码块

概述:

使用大括号{}包起来的一段代码。放在不同的位置,有不同的名称,有不同的作用,有不同的执行时机。

分类:

   局部代码块

   构造代码块

   静态代码块

   同步代码块(多线程)

局部代码块:

格式:使用大括号包起来的一段代码

位置:方法中

作用:

   限定变量的生命周期

   在局部代码块中【声明】的变量,只能在局部代码块的范围内使用,一旦出了局部代码块的大括号,变量就不能继续使用了。

   某个变量一旦不能使用了,就会被回收,节省内存空间

注意事项:

   如果是在局部代码块中声明了变量,会减少变量的声明周期,出了局部代码块就无法继续使用局部代码块中声明的变量。

   如果是在局部代码块中修改了局部代码块外声明的变量,局部代码块结束之后,并不会消除局部代码块对这个变量的修改。

静态代码块

静态代码块:定义在成员位置,使用static修饰的代码块{ }。

位置:类中方法外。

执行:随着类的加载而执行且执行一次,优先于构造方法的执行。

格式:

public class ClassName{

static {// 执行语句}}

API

简介:

API(Application Programming Interface),应用程序编程接口。

Java API是一本程序员的字典 ,是JDK中提供给我们使用的类的说明文档。这些类将底层的代码实现封装了起来,我们不需要关心这些类是如何实现的,只需要学习这些类如何使用即可。所以我们可以通过查询API的方式,来学习Java提供的类,并得知如何使用它们。

使用步骤:

1.打开帮助文档。

2.点击显示,找到索引,看到输入框。

3.你要找谁?在输入框里输入,然后回车。

4.看包。java.lang下的类不需要导包(比如java.lang.String我们使用过,但是没有导它的包),其他需要。

5.看类的解释和说明。

6.学习构造方法。

7.使用成员方法。

接口

概述:

广义:一切定义规则的都是接口

狭义:java中用于定义方法命名的规则就是接口

   Java接口中,全都是方法的声明,都是抽象方法

好处:

   一旦将命名规则定义出来,【方法的调用】和【方法的实现】就分离开了,可以提升开发效率,降低代码的耦合性

特点:

接口的定义:使用interface关键字,编译也是生成一个【.class】文件

   interface 接口名称 {

       方法声明的定义;

}

接口中,只可以声明抽象方法(只能定义方法起名字的规则)

类可以实现接口:使用implements关键字

   实现:接口中只有方法名称的定义,在类中把接口方法的真正完成逻辑写出来

   class 类名称 implements 接口名称 {

       对接口中方法的实现;

}

接口的实例化:不能直接实例化

   定义实现类,实现接口,类创建对象,对象调用方法

接口的实现类前途:

   是一个抽象类,该类没有实现接口中的所有抽象方法

   是一个普通类,该类实现了接口中的所有抽象方法

成员的特点:

成员变量:

   只能是常量,不能是变量

   默认加上public static final

   建议手动加上

构造方法:

   没有构造方法。接口中无法定义成员变量,所以不需要使用构造方法给成员变量初始化赋值。

   虽然接口有自己的实现类,但是对于实现类而言,不去访问接口中的构造方法,而是访问实现类的父类的构造方法。(父类是亲爹、接口是干爹,找亲爹的构造方法,而不是干爹的)

成员方法:

   只能是抽象方法,不能是非抽象方法

   默认加上public abstract

   建议手动加上

final关键字

单词含义:最终的,最后的,表示不能再改变的。

final关键字:可以修饰类、方法、变量

修饰类:

表示一个最终类,表示不能有子类,【不能被其他类继承】

一旦一个类型不能被继承,那么其中所有的方法都不能被重写

不影响当前类的方法被调用

修饰方法:

   表示一个最终方法,【该方法不能被重写】

修饰变量:

   表示一个最终变量,该【变量变成了常量】,就只能赋值一次

   当前项目中,常用的常量一般都定义在一个类的成员位置,甚至专门定义一个常量类

   定义常量的好处:见名知意,容易理解;可维护性高

ArrayList类

到目前为止,我们想存储对象数据,选择的容器,只有对象数组。而数组的长度是固定的,无法适应数据变化的需求。

JDK中提供了一系列特殊的类,这些类可以存储任意类型(不包括基本数据类型)的元素,并且长度可变,统称为集合。

ArrayList类 简介:

ArrayList集合是程序中最常见的一种集合,它属于引用数据类型(类),让我们可以更便捷的存储和操作数据。

在ArrayList内部封装了一个长度可变的数组(可查看ArrayList的源码),当存入的元素超过数组长度时,ArrayList会在内存中分配一个更大的数组来存储这些元素,因此可以将ArrayList集合看作一个长度可变的数组。

ArrayList类 使用:

ArrayList <E> :<E> ,表示一种指定的数据类型,叫做泛型。E ,取自Element(元素)的首字母。在出现E 的地方,我们使用一种引用数据类型将其替换即可,表示我们将存储哪种引用类型的元素。只能存储引用类型(存储的是其地址)的数据,ArrayList不能添加基本数据类型。

若要想要存储基本类型数据<> 中的数据类型,必须转换(转换为包装类)后才能编写:

byte Byte      short Short       int Integer      long Long    

float Float       doule Double  long Long      char Character

boolean Boolean

只有Integer 和Character 需要特殊记忆,其他基本类型只是首字母大写即可

 

格式:ArrayList<String> list = new ArrayList<>();

添加:list.add(String类型的对象)

删除:list.remove(0)移除此列表中指定位置上的元素。返回被删除的元素。

返回指定元素:list.get(1)返回此列表中指定位置上的元素。返回获取的元素。

返回此列表中的元素数:list.size()遍历集合时,可以控制索引范围,防止越界。

Arrays类

概述:

java.util.Arrays 此类包含用来操作数组的各种方法,比如排序和搜索等。其所有方法均为静态方法,调用起来非常简单。

数组名[指定位置]:返回指定数组内容的字符串表示形式。

Arrays.sort(数组名):对指定的int。char数组按数字升序进行排序。

数组名.length:返回数组元素个数。

字符串名.toCharArray:将字符串转换为单一字符的数组“abc”转换后[“a”“b”“c”]

Math类

简介:

java.lang.Math 类包含用于执行基本数学运算的方法,如初等指数、对数、平方根和三角函数。类似这样的工具类,其所有方法均为静态方法,并且不会创建对象,调用起来非常简单。

Math.abs(a);取a(可以是变量,常量)绝对值

Math.ceil(3.3)=4:向上取整,

Math.ceil(-3.3)=-3:向上取整

Math.floor(-3.3)=-4:向下取整

Math.round():四舍五入

E:自然对数的底数,2.718

PI:圆周率

cbrt(double d),返回d的开立方根

sqrt(double d),返回d的正平方根

log10(double d),返回以10为底的d的对数

max(int a, int b),返回a、b的较大值

min(int a, int ),返回a、b的较小值

pow(int a, int b),返回a的b次幂

random(),返回0.000~0.999的随机数

round(double d),返回d四舍五入的结果

继承

在程序中,当多个类中存在相同属性和行为时,可以将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那一个类即可。

其中,多个类可以称为子类,单独那一个类称为父类、超类(superclass)或者基类。

继承:就是子类继承父类的属性行为,使得子类对象具有与父类相同的属性、相同的行为。子类可以直接访问父类中的非私有的属性和行为。

class 子类 extends 父类{ }

继承优点:

  1. 提高代码的复用性。
  2. 提高了代码的可维护性。

2. 类与类之间产生了关系,是多态的前提。

Private只能修饰内部类-----外部类不能用private修饰,不然直接就报错!

单继承:一个子类只能继承一个父类。

原因:

   如果支持多继承,那么可能一个子类继承了两个父类,两个父类中有相同的方法声明,却拥有不同的方法实现。子类继承之后,就不知道该走哪个父类的方法实现了。(安全隐患)

 

父类中的成员变量是非私有的,子类中可以直接访问。若父类中的成员变量私有了,子类是不能直接访问的。通常编码时,我们遵循封装的原则,使用private修饰成员变量(私有变量),可以在父类中提供公共的getXxx方法和setXxx方法。

如果子类父类中出现不重名的成员方法,这时的调用是没有影响的。对象调用方法时,会先在子类中查找有没有对应的方法,若子类中存在就会执行子类中的方法,若子类中不存在就会执行父类中相应的方法。

如果子类父类中出现重名的成员方法,这时是一种特殊情况,叫做方法重写 (Override)。 注意:需要与Overload(重载)区分开

开发原则:面向对象最终要实现的目的

   高内聚(一个对象自己可以完成各种事情)、低耦合(对于其他代码的依赖性非常低),代码的复用性高。

继承中成员变量的关系

在子父类中定义了不同名称的成员变量,在子类中,既可以访问子类的成员变量,也可以访问父类的成员变量。

在子父类中定义了同名的成员变量,在子类中,根据就近原则来访问

在子类的方法中,如果访问了某个变量名称,优先在当前方法中,寻找该变量的定义,如果找到了就使用方法中的局部变量;如果没有找到,就到子类的成员位置寻找该变量的定义,如果找到了就使用子类的成员变量;如果没有找到,就到父类的成员位置寻找该变量的定义....一直往上找,一直到Object类中,如果还没有找到,就编译报错。

继承中成员方法的关系

在子父类中,出现了不同名的方法

子类可以直接调用父类的方法,也可以调用子类的特有方法

在子父类中,出现了同名的方法

   在子父类中,出现了一模一样的方法声明,而两个方法的实现内容却不一样

   作用:在子类中,如果你不想修改父类中的方法声明,但是还想修改方法的实现内容,这时候就需要使用重写。

方法的重写和方法的重载:

重载(Overload):在同一个类中,方法名相同,参数列表不同,与返回值类型无关

重写(Override):在子父类中,方法名相同,参数列表相同,与返回值类型有关(相同)

别名:Override,覆写、覆盖

方法重写的检查:@Override让编译器帮助我们检查,当前的这个方法,究竟是否是对父类方法的重写。

说明:方法本身还是属于父类,只是在子类中重新实现了这个方法的内容。

方法重写 :

子类中出现与父类一模一样的方法时(暂时先这样记:返回值类型,方法名和参数列表都相同),会出现覆盖效果,也称为重写或者复写。声明不变,重新实现。

子类可以根据需要,定义特定于自己的行为。既沿袭了父类的功能名称,又根据子类的需要重新实现父类方法,从而进行扩展增强。

super.父类成员方法,表示调用父类的成员方法,在调用父类方法的基础之上,子类可以进行功能扩展,添加自己的独特功能。

如果子类和父类中没有重名的变量,可直接调用即可。如果有重名的变量,直接调用会调用子类重写父类的变量,若调用父类的可用 super.变量名 调用。

重写的注意事项:

1.私有的方法不能被重写

   父类中私有的方法,在子类中根本就看不到,继承不下来,就不能重写。子类可以自己定义一个和父类私有方法同名的方法,也不算重写,只能算是新定义了一个方法。

   重写:使用父类的方法定义,修改为子类的实现。

2.方法在重写的时候,权限不能越来越小

   子类应该是越来越强大,功能越来越多,将父类中拥有的方法,重写没了。

 

继承后的构造方法

1. 构造方法的名字是与类名一致的。所以子类是无法继承父类构造方法的。

2. 构造方法的作用是初始化成员变量的。所以子类的初始化过程中,必须先执行父类的初始化动作。是这样实现的:子类的构造方法(包括无参构造和有参构造)中默认有一个super() ,表示调用父类的无参构造方法,父类成员变量初始化后,才可以给子类使用。(因为在初始化子类数据的时候,可能会使用到父类中的数据,所以必须先把父类数据准备好)

注意:只能在子类构造方法的第一行调用父类的构造方法,意味着不能重复调用

父类空间优先于子类对象产生

在每次创建子类对象时,先初始化父类空间,再创建其子类对象本身(因为子类要继承父类的属性和方法,如果父类空间不初始化,你继承什么?)。目的在于子类对象中包含了其对应的父类空间,便可以包含其父类的成员,如果父类成员非private修饰,则子类可以随意使用父类成员。代码体现在子类的构造方法调用时,一定先调用父类的构造方法。

如果在子类的构造方法中,手动增加了访问父类的构造方法,那么系统将不给你增加任何构造方法的访问

如果在子类的构造方法中,手动增加了访问本类的其他构造方法,那么系统将不给你增加任何构造方法的访问

构造方法不能递归调用

   构造方法不能自己直接访问自己,也不能自己间接的访问自己

总结:

   1、【目标】子类的构造方法,一定要先访问父类的构造方法

   2、特点:

       【this语句】和【super语句】必须在构造方法的第一行

 super语句在第一句是为了保证父类的构造方法必须先执行

 this语句在第一句是因为间接的访问了super,super必须先执行在构造方法中,【this语句】和【super语句】不能共存

this语句和super语句都要在构造方法的第一行

【this语句】和【super语句】不能出现在其他非构造方法的非静态成员方法中

其他方法都是在对象创建之后,才能调用的方法,如果能调用this语句,就相当于间接的使用对象,调用构造方法

 

 

This、super用法:

this.成员变量‐访问的本类的成员变量

this.成员方法名()‐访问的本类的成员方法

super.成员变量‐访问的父类的成员变量

super.成员方法名()‐访问的父类的成员方法

this,super含义:

this关键字表示本类当前对象的引用

    哪个对象在调用this所在的方法,this就表示哪个对象

super关键字表示本类当前对象的父类的引用

    哪个对象在调用super所在的方法,super就表示哪个对象中的父类部分的数据

super和this都可以访问成员变量

 super只能访问父类中定义的成员变量

    super.成员变量名

 this既可以访问子类中定义的成员变量,也可以访问父类中定义的成员变量

    this.成员变量名

super和this都可以访问成员方法

 super只能访问父类中定义的成员方法

    super.成员方法名()

 this不仅可以访问子类中定义的成员方法,也可以访问父类中定义的成员方法

    this.成员方法名()

super和this都可以访问构造方法:this语句和super语句

 this():访问本类的其他构造方法

 super():访问父类的构造方法

继承的注意事项

私有的成员不能被继承

   父类中有一些私有成员,不能在子类中直接使用

   只不过在子类中,不能直接访问父类中定义的私有成员变量

父类中的构造方法,不能继承

   原因:

       父类的构造方法需要和父类的类名一致、子类的构造方法需要和子类类名一致,父类和子类的类名不一样。因此无法继承,名称有冲突。

   父类的构造方法用于给父类的成员变量赋值,子类的构造方法用于给子类的成员变量赋值,子类的成员变量较多,使用父类的构造方法无法将子类中所有的成员变量都进行赋值,因此不继承父类的构造方法。

解决:

   子类不继承父类的构造,但是可以【调用】父类的构造方法。

 

 

类与类、类与接口、接口与接口之间的关系

类与类:

继承的关系,使用extends

可以单继承、不可以多继承、可以多层继承

类与接口:

实现关系,使用implements

可以单实现、也可以多实现、不能多层实现(一旦实现变成了一个类)

多实现的格式:  

   class 实现类类名 implements 接口1, 接口2, 接口3.......{

       重写所有接口中的所有抽象方法

}

多实现没有安全隐患:即使两个接口中有一样的方法声明,但是在类中也只有一个实现

在继承一个父类的前提下,还可以实现多个接口

   格式:      

       class 子类类名 extends 父类类名 implements 接口1, 接口2.....{    //重写父类和所有接口中的所有抽象方法}

接口与接口:

继承关系,使用extends

可以单继承、也可以多继承、可以多层继承

多继承的格式:  

interface 接口名 extends 父接口1, 父接口2.....{

   相当于继承了所有父接口的所有抽象方法

}

类和接口的区别(设计区别):

抽象类:定义物体本身具有的固有属性和行为

接口:定义物体通过学习、训练而扩展出来的行为

内部类

内部类的概述

定义在内部的类,就是内部类。可以定义在方法的内部,可以定义在类的内部。

根据定义的位置不同,可以分为:

   成员内部类

   局部内部类

成员内部类:

   普通的成员内部类

   私有的成员内部类

   静态的成员内部类

局部内部类

根据表示的方式不同,可以分为:

   有名字的内部类

   匿名内部类

普通的成员内部类

定义在成员位置上的类,就是成员内部类

定义格式:

   class 内部类类名 {

       内部类成员

}

成员内部类的说明:

   1、内部类可以直接访问外部类的所有成员,包括私有成员

   2、外部类访问内部类的成员,必须先创建内部类的对象

   3、在外部类以外,创建内部类的对象的格式:    

       外部类名.内部类名 内部类对象名 = new 外部类名().new 内部类名();

私有的成员内部类

也是一个成员内部类,在成员内部类前面加上一个private关键字

访问方式说明:

   1、在外部类以外,不能直接访问外部类中的私有成员内部类

   2、可以在外部类中,定义一个访问私有成员内部类的公有方法,让外界可以调用公有方法,间接的访问外部类中的私有成员内部类。(在同类中内部类外的成员方法中创建这个内部类的对象,调用公用方法,简介访问外部的成员变量)

静态的成员内部类

也是一个成员内部类,在成员内部类前面加上一个static关键字

访问特点:

   1、成员内部类是外部类的静态成员,所以可以通过外部类名.内部类名的方式直接访问,而不需要创建外部类的对象

   2、静态内部类中的非静态成员,需要将所在的内部类对象创建出来之后,才能被调用。

   3、总结:一个类是否需要创建对象,不取决于该类本身是否是静态,而取决于要访问的成员是否是静态

3、静态成员内部类的对象创建格式:

   外部类名.内部类名 内部类对象名 = new 外部类名.内部类名();

局部内部类

局部内部类:定义在方法中的内部类

局部内部类的访问说明:

   方法中的局部变量,外界都没有办法访问到

   所以方法中定义的内部类,外界也没有办法访问到

解决方式:

   在方法内部,就创建局部内部类的对象,调用对象的方法

   外界调用局部内部类所在的方法,间接的创建局部内部类的对象,间接的访问局部内部类的方法

匿名内部类、、(重点)

没有名字的内部类

匿名内部类的使用前提:

   匿名类:继承一个类

   匿名类:实现一个接口

格式:

   new 父类类名或者接口名() {

       父类方法的重写或者是接口内容的实现

}

本质:创建了一个类的子类对象、接口的实现类对象

内部类的名称总结:

   1、如果是有名字的内部类,类名【外部类名$内部类名】

   2、如果是没有名字的内部类,通过匿名内部类的顺序描述类名,第一个匿名内部类的名称【外部类名$1】,第二个匿名内部类类名【外部类名$2】

Eclipse快捷键

调整字体大小

   增加大小:ctrl + 【+】

   减小字体:ctrl + 【-】

Ctrl + n:新建工程、包、类和文件等内容

Ctrl + shift + f:格式化代码,规范对齐

Ctrl + shift + o:自动导包、删除没有用的包

Ctrl + /:单行注释,取消单行注释

Ctrl + shift + / :多行注释

Ctrl + shift + :取消多行注释

Ctrl+shift+t:快速打开某个类

Alt + ↑:向上移动一行

Alt + ↓:向下移动一行

Ctrl + alt + ↑:向上复制一行

Ctrl + alt + ↓ :向下复制一行

Ctrl + d:删除一整行

Alt + shift + r:整体修改某个标识符名称

F2:可以修改文件名称,并且将所有关联的位置一起修改

Alt + shift +M :将选定的内容用方法包含进去

Alt + shift + s:

   C:生成空参构造

   O:生成有参构造

   R:生成getset方法,配合alt + a、alt + r

权限修饰符

就是一些关键字,修饰成员,用于决定这些成员可以在什么位置被访问

这些关键字都是“封装”的体现形式

private:                       只能在本类中被访问

默认的权限修饰符:              可以在本类中被访问

Default(默认可以不写)         也可以在本包中的其他类中被访问

protected:                     可以在本类中被访问

                                   也可以在本包中的其他类中被访问

                                   可以在其他包的子类中被【继承】

public :                       能在本类中被访问

                                   能在本包中的其他类中被访问

                                   能在其他包中的子类中被访问

                                   能在其他包的无关类中被访问

 

Object类

Object类是所有类型的顶层父类,所有类型的直接或者间接的父类;所有的类型中(包括数组)都含有Object类中的所有方法。

随意定义一个类型,不手动显式定义其父类,那么这个类的父类就是Object类

Object类的构造方法:Object()

   1、可以自己创建对象

   2、让子类访问,所有子类都会直接或者间接的访问到这个顶层父类的构造方法

   3、Object类在执行构造方法时,不去访问自己的父类,因为没有父类了

getClass()方法

返回当前对象的运行时类(返回当前对象的实例化的类名)

运行时类:真正创建对象所使用的那个子类类型。写代码的时候可能使用的是父类类型或者是接口类型的引用。

返回值类型:Class类型的对象(不详细解释),就是加载到方法区的那个字节码对象。

其中Class类型中有一个getName方法,可以返回当前类型的全类名

hashCode方法

根据对象的情况,生成的一个整数,就是哈希码值。生成数字的方法就是hashCode方法

生成数字的原则:

   1、同一个对象多次调用hashCode方法,【必须】返回相同的整数(程序多次运行不要求哈希码值保证一致  )

   2、使用equals方法判断相同的两个对象,【必须】返回相同的整数(equals方法是用于决定两个对象是否相同的方法)

   3、使用equals方法判断不相同的两个对象,【尽量】返回不相同的整数(不做强制要求)

   4、Object类型的hashCode,【确实】会根据不同的对象生成不同的整数。

toString方法

返回当前对象的字符串表示

Object类型中,这个方法的实现:全类名 + @ + 哈希码值的十六进制表示

   getClass().getName() + “@” + Integer.toHexString(hashCode());

   简称这个内容为:对象的地址值

对象返回这样一个地址值的字符串,没有什么意义,因此对于子类而言,需要重写父类的这个方法。

重写的原则:返回该对象中的所有成员变量的值(对象的属性)

最终操作:不需要手动重写,可以直接使用快捷键生成:

   Alt + shift s s

使用打印语句打印一个对象,其实打印的就是这个对象的toString内容

equals方法

用于比较两个对象是否相等的方法,比较的就是“调用者”和“参数”这两个对象

   boolean equals(Object obj)

性质:

   自反性:自己和自己相等

   对称性:A和B相等,那么B就和A相等。A.equals(B)为true,那么B.equals(A)也为true

  传递性:A.equals(B)为true,并且B.equals(C)为true,那么A.equals(C)也为true

   一致性:A多次调用equals方法用于和B对象比较,应该返回相同的判断结果

在Object类型中,比较的是两个引用是否指向了同一个对象。如果是,才返回true。相当于是在比较两个对象的地址值是否相同。

实际生活中,比较两个对象的内存地址,没有什么意义。因此在自定义的子类中,都要重写这个方法。

重写原则:

   一般比较两个对象中的所有属性,是否全部相同

操作:

   不需要手动自己重写equals方法,直接使用eclipse中的快捷键即可

   Alt + shift + s      h

==和equals方法的区别

==和equals都是用于比较数据是否相等的方式

不同点:

   1、比较内容的不同:

       ==可以比较任意数据类型,既可以比较基本数据类型,也可以比较引用数据类型

       equals方法只能比较引用数据类型

   2、比较规则不同:

       ==在比较基本类型的时候,比较的是数据的值;比较引用类型时,比较的是地址值

       equals方法在重写之前,比较的是两个对象的地址值;在重写之后,比较的属性值

Scanner类

Scanner就是一个扫描器,可以扫描指定设备的基本数据类型和字符串

构造方法:

   Scanner(File f):扫描指定文件

   Scanner(String path):扫描指定的路径

   Scanner(InputStream is):扫描指定的输入流

   其中学习过的第三个:Scanner(System.in),其中的System.in就是一个输入流,标准输入流,默认关联到键盘。

扫描器可以解析基本数据类型和字符串

录入基本数据类型

录入基本数据类型的方法:

   nextByte():获取一个byte数据

   nextShort():获取一个short数据

   nextInt()

   nextLong()

   nextFloat()

   nextDouble()

   nextBoolean()

注意事项:

   1、没有nextChar这个方法,一般都是直接录入字符串

   2、键盘录入的数据,不是java代码,所以float类型的常量不需要加f

   3、一次想录入多个数据,可以使用空格进行分隔

录入字符串类型

next():可以录入下一个完整的标记,返回一个字符串。通过空格来分隔各个标记。

nextLine():可以录入下一个完整的标记,返回一个字符串。通过换行符来分隔各个标记。

String类

String就是字符串类型,属于java.lang包,不需要导包

所有的字符串常量(“Hello World”、”abc”)都属于String类型的对象

字符串字面值属于常量,存储在方法区的常量池中。

String类型是一个常量,在创建之后就无法更改(是一个不可变的字符序列)。

不可变的原因是String类型只提供了构造方法,没有提供set方法,因此只能在创建对象的时候,初始化成员变量,将来对象创建完成之后,无法通过方法来修改。

String类型的构造方法

String():创建一个空字符串

String(String original):创建参数字符串的一个副本(参数字符串是在常量池中,构造方法创建的字符串是在堆内存中)

String(byte[] arr):将一个字节数组转成一个字符串

   将我们不认识的字节数组,转成了我们认识的字符串,过程叫做【解码】

   查询的是当前平台默认的编码表

String(byte[] arr, int offset, int length):将字节数组的一部分转成字符串

String(char[] arr):将字符数组转成字符串

既不是编码,也不是解码,只不过是把字符串成了串

String(char[] arr, int offset, int length):将字符数组的一部分转成字符串

String类型的判断功能

equals(Object obj):判断调用者和参数对象描述的字符串内容是否相同

equalsIgnoreCase(String otherStr):忽略大小写判断两个字符串内容是否相同

contains(String str):判断调用者是否包含了str这个子串

startsWith(String prefix):判断调用者是否以prefix开头

endsWith(String suffix):判断调用者是否以suffix结尾

isEmpty():判断调用者是否是空串

String类型的获取功能

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

charAt(int index):返回调用者字符串中索引为index的字符(和length方法结合之后可以遍历字符串)

substring(int beginIndex):获取一个字符串,内容是从当前字符串的beginIndex索引开始

substring(int beginIndex, int endIndex):获取一个指定索引范围的子串

   注意事项:1、包含头不包含尾,返回的结果中,不包含endIndex索引指向的字符;2、所有的方法都无法修改字符串对象本身,一般都是返回一个新的字符串对象

indexOf家族:

   indexOf(int ch):返回ch字符在当前调用者字符串中,第一次出现的索引

   indexOf(int ch, int fromIndex):从fromIndex索引开始寻找,找到ch字符在当前字符串中第一次出现的索引

   indexOf(String str):返回的是str这个字符串在调用者字符串中第一次出现的索引

   indexOf(String str, int fromIndex):从fromIndex索引开始寻找,找到str字符串在当前字符串中第一次出现的索引(注意:无论从哪个位置开始找,所有字符的索引都不会变化)

lastIndexOf家族:

   和IndexOf基本一样,只不过是从后往前找,所有字符和字符串的索引也都不会发生变化

String类型的转换功能

byte[] getBytes():将当前字符串,转成字节数组

char[] toCharArray():将当前的字符串,转成字符数组

toUpperCase():将当前的字符串,转成全大写形式

toLowerCase():将当前的字符串,转成全小写形式

concat(String str):将当前调用者,和参数str进行拼接,返回拼接后的长字符串(不常用,因为更多使用的是运算符+)

valueOf家族:可以将任意数据类型的数据,转换成字符串

String类型的其他方法

replace(String oldStr, String newStr):将调用者中的老串替换成新串

trim():去掉字符串左右两边的空格、制表符

StringBuilder类

StringBuilder是一个可变的字符序列,因为在类中提供了修改私有成员变量的方法

常用的方法是append和insert,就是在StringBuilder对象本身上,进行修改操作

StringBuilder底层和String类型一样,也是维护了一个字符数组,数组是私有的,外界无法直接访问,因此在StringBuilder中对数组进行操作的公有方法的封装

String类型和StringBuilder的关系:都是用于描述字符串

   1、String是不可变的字符序列,没有提供修改私有成员的方法;StringBuilder是可变的字符序列,因为提供了修改成员变量的方法;

   2、String长度本身也不可以变化,StringBuilder长度可以变化,可以认为StringBuilder就像一个可以伸缩的容器,用于存储字符

构造方法

构造方法作用:创建当前对象、将其他类型的数据,转换成当前类型

StringBuilder的构造方法:

   StringBuilder():创建一个生成器,初始容量为16个字符

   StringBuilder(int capacity):创建一个生成器,初始容量为capacity大小

   StringBuilder(String str):创建一个生成器,初始值就是str这些字符,初始大小是str+16

获取容积的方法:

   capacity():获取当前生成器的容器大小

   length():获取当前生成器中的字符个数

添加功能

append(任意类型):可以将任意数据类型,转成字符,添加到生成器中

insert(int index, 任意数据类型):可以将任意数据类型,添加到指定的位置。

说明:1、index的范围是0~当前缓冲区的大小;2、插入数据之后,数据本身的索引就是参数中指定的index

删除功能

deleteCharAt(int index) :删除指定索引的字符

delete(int start, int end):删除指定范围的字符,被删除的部分包含头不包含尾

替换和反转功能

replace(int start, int end ,String str):

   将字符串缓冲区中的从start开始到end-1这部分内容,替换成str。

reverse():将原有字符序列进行反转

String和StringBuilder拼接字符串的区别

String是常量,每次对String字符串的修改都会创建一个对象

StringBuilder是一个长度可变的字符串,每次修改都是对原有对象的修改

String和StringBuilder的相互转换

1、String转成StringBuilder

1、StringBuilder的构造方法 例:

String str = "abc";

       StringBuilder sb = new StringBuilder(str);

   2、append方法:将指定的字符串附加到此字符序列。

2、StringBuilder转成String类型

   1、toString的方法

String result = sb.toString();

   2、使用String的构造方法:分配一个新的字符串,其中包含当前包含在字符串构建器参数中的字符序列。

StringBuffer和StringBuilder的区别

共同点:

   都是字符串的缓冲区,都是字符串的生成器,都是可变的字符序列

不同点:

   1、出现版本不同:

       StringBuffer在jdk1.0出现的

       StringBuilder在jdk1.5出现的

   2、线程安全性不同:

       StringBuffer是线程安全的,在多线程环境下仍然保证数据安全

       StringBuilder是线程不安全,在多线程环境下无法保证数据安全

   3、效率不同:

       StringBuffer效率低

       StringBuilder效率高

Integer的构造方法

Integer(int i):将一个基本类型的int数,转换成Integer类型的对象

   使用i给Integer对象中的成员变量赋值

Integer(String s):将一个字符串类型的数字,转换成Integer类型的对象转换的作用

Integer类型的成员方法

xxxValue():可以将Integer类型的对象,转成其他的基本数据类型:

   例如:byteValue()、floatValue()

integerType.基本数据类型.Value();

常用静态方法:

   parseInt(String str):将str以十进制的方式解读为一个int数

   parseInt(String str, int radix):将str以指定的radix进制解析为一个int数字

   toBinaryString(int i):使用二进制的表示方式表示数字i

   toOctalString(int i):使用八进制的表示方式表示数字i

   toHexString(int i):使用十六进制的表示方式表示数字i

   toString(int i, int radix):使用指定的radix进制,表示数字i

   valueOf(String str, int radix):将str以指定的进制radix进行解析,封装为一个Integer

Integer、int、String类型互相转换的总结

Integer转换成int类型

   intValue()

int转换成Integer类型

   构造方法Integer(int i)

Integer到String的转换

   toString方法即可

String到Integer的转换

   构造方法Integer(String str)

   Integer.valueOf

int转换成String类型:

   Integer.toString(int i)

   拼接一个空串

String转换成int类型

   Integer.parseInt(String str)

装箱和拆箱:

   装箱:将基本数据类型,封装成包装类型的对象,这个过程就是装箱,使用构造方法即可

   拆箱:从包装类型的对象中,将其包装的基本类型的数据取出,这个过程就是拆箱,使用intValue方法即可

System类

用于描述系统资源的类型,该类不用创建对象,直接使用静态变量和静态方法即可

常用字段:

   System.in:标准输入流,默认关联到键盘上

   System.out:标准输出流,默认关联到控制台上

   System.err:标准错误输出流,默认关联到控制台上,用于打印错误信息,在eclipse中,使用该流打印的内容是红色

常用方法:

gc():强制垃圾回收器回收内存中的垃圾

exit(int status):结束虚拟机

currentTimeMillis():返回当前时间的毫秒值,表示的是从1970年1月1日0时0分0秒开始到现在经历的毫秒值。1s = 1000ms

作用:1、可以通过某些手段,将数字转换成时间对象、指定格式的字符串;2、可以通过计算两次的差值,来获取某段代码运行的时间

Date类

该类对象用于表示一个特定的瞬间,根据构造方法使用的不同,表示不同的瞬间

构造方法:大部分构造方法都已经过时(可以使用,但是不建议使用,eclipse中有横线)

   Date():表示当前时间

   Date(long time):表示一个时间对象,表示的是从1970年1月1日00:00:00时间经过time毫秒之后的时刻

成员方法:

   after(Date d):比较调用者是否在在参数时间d之后

   before(Date d):比较调用者是否在参数时间d之前

   getTime():获取当前Date对象描述的时间的毫秒值

   setTime(long time):将制定的毫秒值给当前Date对象进行设置

DateFormat

直接打印Date对象全是英文,不好阅读,需要有一个格式对象,将Date对象的内容以指定的格式输出出来。

DateFormat类,抽象类,不能创建对象,需要使用其子类创建对象,有一个已知子类,SimpleDateFormat类型。

DateFormat类型在text包中,在使用的时候需要导包

构造方法DateFormat():用于给子类去访问

重要成员方法:

   String format(Date d):将参数对象d,格式化为一个字符串

   Date parse(String str):将参数字符串str,解析成一个Date类型的对象

SimpleDateFormat

DateFormat是抽象类,无法创建对象,所以只能使用其子类来创建对象,并调用方法,使用SimpleDateFormat类型,可以使用DateFormat中的所有方法。

构造方法:

   SimpleDateFormat():使用默认的格式创建格式对象,默认格式为:18-12-20 下午2:20

     SimpleDateFormat(String pattern):使用给定的模式创建格式对象

 

Calendar

表示一个精确的瞬间,是一个包含了很多字段值的对象。

在util包中,所以在使用的时候需要导包

Calendar是一个抽象类,无法直接创建对象:

1、自己使用子类来创建对象

2、使用父类中的某个方法,来获取一个对象

4、Calendar获取对象的方式:   

Calendar.getInstance():可以获取当前时间的Calendar对象,就已经把各个日历字段都初始化完成了

常用方法:

   get、set方法用于给对象时间赋值

   add方法可以给对象的某个字段增加值

   getTimeInMillis获取对象描述的毫秒值

Calendar的get方法

直接打印Calendar对象,显示所有字段的值,只需要获取某个确定字段的值,就使用get方法。

int get(int field)

   传入一个表示字段顺序的值,获取一个这个字段的值

   参数其实就是一个数字,表示一个顺序,但是不方便记忆,将这些顺序都定义成了Calendar类型中的常量,使用常量名称替代顺序数字即可。

说明:

   获取的月份,从0-11值,获取出来之后+1才是我们中国人习惯的月份

   获取的星期,周日是第一天,需要做一些其他的转换才能变成习惯的星期

private static void test2_get方法() {

获取当前时间的Calendar对象,并完成初始化

Calendar c = Calendar.getInstance();

int year = c.get(Calendar.YEAR);获取年份

int month = c.get(Calendar.MONTH) + 1;获取月份(0-11)

int date = c.get(Calendar.DATE);获取当月几号

int hour = c.get(Calendar.HOUR_OF_DAY);获取小时数

int minute = c.get(Calendar.MINUTE);获取分钟数

int second = c.get(Calendar.SECOND);获取秒数

int week = c.get(Calendar.DAY_OF_WEEK);获取星期几(0-6)(七-六)

String weekStr = getWeek(week);                一一对应

System.out.println(year + "年" + month + "月" + date + "日" + hour + "时" + minute + "分" + second + "秒,是" + weekStr);

private static String getWeek(int week) {

       String[] weeks = {"", "星期天",  "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};

       return weeks[week];

set方法

get方法是用于给一个字段,获取字段的值,set方法,根据给定的字段和字段值,修改Calendar类型的对象。

void set(int field, int value)

   将指定的字段,设定为指定的值

   注意:如果写了不正确的时间值,会自动的进位

void set(int year, int month, int date)

   给当前Calendar对象设定年月日

void set(int year, int month, int date, int hourOfDay, int minute)

   给当前日历对象设定年月日时分

void set(int year, int month, int date, int hourOfDay, int minute, int second)

   给当前日历对象设定年月日时分秒

add方法

方法功能:在某个字段的基础上,进行偏移(描述的是变化,而不是变化的结果)

void add(int field, int offset)

   在field字段原来值的基础上,增加offset量

毫秒值、Date类型和Calendar类型的相互转换

毫秒值和Date的相互转换

   毫秒值转成Date对象:Date类型的构造方法,Date(long time)

   Date对象转成毫秒值:Date类型的getTime()方法

毫秒值和Calendar类型的相互转换

   毫秒值转Calendar类型:setTimeInMillis(long time)

   Calendar类型转毫秒值:getTimeInMillis()

Date和Calendar类型的相互转换

   Calendar类型转成Date对象:getTime()

   Date对象转成Calendar类型:setTime(Date d)

集合的体系结构

集合分类:

单列集合:每个元素都是一个单独的个体

双列集合:每个操作都是针对一对数据来进行的,一对数据作为一个单位

单列集合的体系:

   Collection                   单列集合的顶层接口

       List                     有序的子接口

           ArrayList             顺序存储,查询修改快

           LinkedList            链式存储,增加删除快

           Vector                 顺序存储,都慢

       Set                      无序的子接口

           HashSet              哈希存储

               LinkedHashSet     有序哈希储存

双列结合的体系:

   Map                      双列集合的顶层接口

       HashMap                哈希表存储

           LinkedHashMap     有序哈希表储存

Collection

概述和常用方法

单词:收集、集合

单列集合的顶层接口,定义的是所有单列集合中共有的功能。

Collection是一个接口,不能直接创建对象,随意找一个实现类创建对象

使用接口类型的引用,指向实现类的对象

Collection类型的引用,指向 ArrayList类型的对象(只能调用接口中的方法)

常用方法:

add(Object obj):将obj元素添加到集合中

remove(Object obj):将obj元素从集合中删除

clear():将集合中的元素清空

isEmpty():判断集合是否为空

contains(Object obj):判断集合中是否包含obj元素

size():返回集合中的元素个数

Collection的第一种遍历方式

转成数组,通过遍历数组的方式,来间接的遍历集合

Object[] toArray():将调用者集合转成Object类型的数组

Object[] 数组名 = Object对象名.toArray();

Collection中带all的方法

addAll(Collection c):将参数c中的所有元素,都添加到调用者集合中

removeAll(Collection c):从调用者集合中,删除那些也存在于参数c中的元素

containsAll(Collection c):判断调用者,是否能包含参数c中的所有元素

retainAll(Collection c):参数c中有哪些元素,就在调用者集合中,保留哪些元素(交集)

集合遍历的第二种方式:迭代器

迭代:更迭、更新换代,有从某一个到下一个的过程的含义

迭代器:专门用于将集合中的元素,一个到另一个逐个进行迭代的对象

获取:集合自己内部就应该有一个可以迭代自己的对象,从集合对象中获取即可

   Iterator  iterator()

迭代器的使用:

   方法iterator返回的是一个Iterator接口的实现类对象,可以使用的就是Iterator接口中的方法:

   hasNext():判断集合中是否还有下一个元素

   next():获取集合中的下一个元素

   remove():删除迭代器对象正在迭代的那个对象

迭代器的注意事项:

   1、迭代器对象虽然多次调用next方法,都是同样的方法名称,但是每次调用返回的结果是不同的,会因为next方法既可以获取下一个元素,也会让迭代器对象,向前移动一步。

   2、如果没有下一个元素,仍然调用next方法,出现NoSuchElementException(没有当前元素异常),可以使用hasNext方法判断是否有下一个元素,决定是否调用next方法

   3、hasNext方法,判断是否有下一个元素,不会移动迭代器的位置

   4、next方法,不仅可以获取下一个元素,还会移动迭代器位置

   5、不要只判断一次hasNext,就调用多次next方法

List

是Collection的一个子接口

特点:

   有序:每个元素都有自己的位置,不同位置是有分别的

   【有索引】:每个元素都有自己的编号

   可以重复:即使是值相同的几个元素,位置和索引也各不相同,可以区分这几个值。

特有方法:

   add(int index, Object obj):在指定索引上,添加指定的元素

   remove(int index):删除指定索引上的值

   set(int index, Object obj):将指定索引上的值,修改为指定的值

   get(int index):根据给定的索引,获取对应位置的值

第三种遍历方式

针对List集合特有的遍历方式

可以通过集合的size方法获取list集合索引的范围,根据索引通过get方法可以获取指定索引的值。

并发修改异常

ConcurrentModificationException

     并发       修改      异常

出现原因:

     在使用【迭代器对象】遍历集合的同时,使用【集合对象】增加集合的元素

解决方法:两种解决方式都是针对List集合可以解决

     方式1:迭代器遍历、迭代器增加

     方式2:集合遍历、集合增加

解决方式1:迭代器遍历、迭代器增加

     迭代器遍历,和以前一样

     迭代器增加:问题普通的迭代器中没有增加的方法,需要使用List中特有的迭代器

     列表迭代器:ListIterator,是Iterator的子接口,拥有Iterator中的所有方法,还有特有

     列表迭代器的获取:listIterator()

解决方式2:集合遍历、集合添加

     集合遍历:list特有的方式遍历,size和get方法结合

     集合添加:集合的add方法

List的实现类

List只是一个接口,根据底层实现方式的不同,具有不同的实现类

ArrayList:数组实现,顺序存储

LinkedList:节点实现,链式存储

Vector:数组实现,顺序存储

Vector

在jdk1.0版本出现,现在这个类已经过时,在jdk1.2之后,被ArrayList取代

特点:

     线程安全,效率较低

顺序存储,增删较慢

特有方法:

     addElement(Object obj):添加元素

     removeElement(Object obj):删除元素

     elements():获取Vector集合的枚举对象,用于遍历集合

特有遍历方式:

     1、使用elements方法获取Enumeration对象

     2、使用Enumeration对象的hasMoreElements方法判断是否有下一个元素

     3、如果有下一个元素,就使用nextElement方法来获取下一个元素

ArrayList

也是List的一个实现类

没有什么特有方法

存储方式:

     数组实现,顺序存储

     通过物理内存的位置关系,来表达描述逻辑顺序的相邻

LinkedList

List的一个实现类

存储方式:

   节点实现,链式存储

   不通过物理内存位置的相邻,来表示逻辑顺序的相邻

   每个元素都存储在一个节点中,节点除了元素数据本身以外,还需要存储下一个元素的内存地址

特点:

   查询速度慢:需要根据前面的节点来获取后一个节点的地址,前面所有节点都要访问一遍,节点数量越多,查询速度越慢

   增删速度快:增删一个元素,只需要修改新增元素前后的两个节点的引用域即可,与集合本身的元素个数无关。

LinkedList的特有方法:

   由于在LinkedList中,维护了链表的头和尾节点的对象地址,所以操作头部和尾部非常容易,提供了大量的操作头和尾的方法。

   addFirst(Object obj):在头部添加元素

addLast(Object obj):在尾部添加元素

   removeFirst():删除头部元素

   removeLast():删除尾部元素

   getFirst():获取头部元素

   getLast():获取尾部元素

泛型

泛型的概述和使用【熟练掌握】

泛型:广泛的类型,在定义一个类的时候,类型中有些方法参数、返回值类型不确定,就使用一个符号,来表示那些尚未确定的类型,这个符号,就称为泛型。

使用:对于有泛型的类型,在这些类型后面跟上尖括号,尖括号里面写上泛型的确定类型(在使用某个类创建对象时,已经可以确定这个具体的类型了,那么就直接写出具体类型)

   例如:ArrayList<Integer> al = new ArrayList<Integer>();

泛型的好处:

   1、提高了数据的安全性,将运行时的问题,提前暴露在编译时期

   2、避免了强转的麻烦

4、注意事项:

   1、前后一致:在创建对象时,赋值符号前面和后面的类型的泛型,必须一致

   2、泛型推断:如果前面的引用所属的类型已经写好了泛型,后面创建对象的类型就可以只写一个尖括号,尖括号中可以不写任何内容。<>特别像菱形,称为“菱形泛型”,jdk1.7特性

泛型类的定义

泛型类:带着泛型定义的类

格式:

   class 类名<泛型类型1, 泛型类型2, .....>  {

}

说明:

   1、类名后面跟着的泛型类型,是泛型的声明,一旦泛型声明出来,就相当于这个类型成为了已知类型,这个类型就可以在整个类中使用

   2、泛型的声明名称,只需要是一个合法的标识符即可,但是通常我们使用单个大写字母来表示,常用字母:T、W、Q、K、V、E

   3、泛型确定的时机:将来在使用和这个类,创建对象的时候

泛型方法的定义

在方法声明中,带着泛型声明的方法,就是泛型方法

格式:

   修饰符 <泛型声明1, 泛型声明2,.....> 返回值类型 方法名称(参数列表) {

 

}

说明:

   1、在方法上声明的泛型,可以在整个方法中,当做已知类型来使用

   2、如果【非静态】方法上没有任何泛型的声明,那么可以使用类中定义的泛型

   3、如果【静态】方法上没有任何的泛型声明,那么就不能使用泛型,连类中定义的泛型,也不能使用,因为类中的泛型需要在创建对象的时候才能确定。所以【静态】方法想使用泛型,就必须在自己的方法上单独声明。

泛型接口的定义和使用

带着泛型定义的接口,就是泛型接口

定义格式:

   interface 接口名称<泛型类型1, 泛型类型2,...> {

 

}

说明:

   1、在接口声明上,定义好的泛型,可以在整个接口中当做已知类型来使用

   2、泛型接口被其他类实现的时候,有两种实现方式:    

       1、声明的类不再是一个泛型类,而是一个确定了泛型的类,格式如下:               class 实现类类名 implements 接口名<具体类型> {

               所有的泛型类型都已经被确定

}

       2、声明的来还是一个泛型类,泛型的类型和接口的泛型一致,格式如下:

           class 实现类类名<泛型标识符1> implements 接口名<泛型标识符1> {

             所有的方法还都是可以使用泛型标识符的方法。

}

集合体系中,大量的接口和实现类都存在实现和继承的关系

   Collection<E>...继承关系...List<E>....实现关系....ArrayList<E>

泛型的通配符【了解】

使用泛型的时候,没有使用具体的泛型声明T,而是使用了和声明过的某个泛型T有关的一类类型,就称为泛型的通配符。三种形式:

第一种形式,使用?来表示可以是任意类型,例如:

Collection<E>接口中的removeAll(Collection<?> c),表示可以接收任意泛型类型的集合,作为该方法的实际参数,参数集合的泛型,可以是与E没有任何关系

第二种形式,使用? extends E来表示必须是某个泛型类型或是该泛型类型的子类,例如:

Collection<E>接口中的addAll(Collection<? extends E> c),表示可以接收泛型类型是调用者泛型类型或者其子类的集合,作为该方法的实际参数。参数的泛型和调用者的泛型,必须有关(相同或者是子父类)。确定了泛型的上边界。

第三种形式,使用? super E来表示必须是某个泛型类型或者是该泛型类型的父类,例如:

   TreeSet<E>集合中,存储的都是E类型的元素,构造方法TreeSet(Comparator<? super E> com),表示可以接收泛型类型是集合元素类型或者是元素类型的父类的比较器,作为构造方法的参数。参数的泛型和集合的泛型,必须有关(相同或者是子父类)。确定了泛型的下边界。

Set

Set的概述

Set是Collection的另一个子接口

特点:

   无序:没有任何前后的分别,所有的元素没有位置的概念,所有的元素都在集合中

   没有索引:集合中没有任何位置,元素也就没有位置的属性

   不能重复:没有位置的区分,相同值的元素没有任何分别,所以不能重复

Set的实现类:

   HashSet:使用哈希表的存储方式存储元素的Set集合

存储特点:

   1、相同的元素无法存储进Set集合

   2、集合本身不保证顺序:存储的顺序和取出的顺序不保证一致

Set集合的遍历

没有自己特有的方法,只能使用Collection接口中定义的方法,只能使用Collection的遍历方式

第一种:转成数组,toArray(),不带泛型的转数组,得到的是Object类型的数组。

第二种:转成数组,toArray(T[] arr),带泛型的转数组,得到的是T类型的数组。

   1、自己创建的数组大小,小于集合元素个数

       在方法中,就只能创建一个新的数组,使用集合元素将数组填充,将新数组返回

   2、自己创建的数组大小,等于集合元素个数

     在方法中,就不需要创建新的数组,使用集合元素将传入的数组填充,将原数组返回

   3、自己创建的数组大小,大于集合元素个数

      在方法中,也不创建新的数组,直接将传入的数组前面几个位置填充,为集合的元素,剩余位置使用null填充(默认值就是null)

第三种:迭代器

第四种:增强for循环

   格式:

       for (元素的数据类型 元素名称 : 要遍历的集合) {

           使用元素名称代表当前访问的元素

}

   说明:

       元素的数据类型,值得是要遍历的集合中的元素的数据类型

    元素名称虽然是固定的,但是随着循环的执行,每次代表的元素却不相同

       要遍历的集合,单列集合或者是数组

   本质:

       底层还是迭代器,只不过使用这种格式更加简洁

   注意事项:

       使用增强for,没有拿到元素的索引,无法修改集合或者数组中的元素值

       底层是迭代器,所以在遍历的时候,使用集合对象增加元素,会发生并发修改异常

HashSet保证元素唯一性的原理

HashSet存储jdk提供的类型的元素,发现直接保证了元素的唯一性,值相同的元素都去掉了

HashSet存储自定义类型的元素

实验过程:

   1、HashSet存储自定义类型的元素,发现并没有保证元素的唯一性

   2、怀疑在比较元素的时候,通过Object类中的equals方法比较的,比较的是两个对象的地址值,所以地址值都是不相同的,因此去重没有成功。

  3、重写equals方法,比较对象中的数据是否全部相同。发现重写之后,equals方法没有调用

   4、考虑HashSet使用了哈希存储来存储元素,考虑和hashCode方法可能有关,重写hashCode方法。

   5、在重写hashCode方法之前,调用的是super.hashCode,其中super指的是Object类型,Object类型可以根据不同的对象,生成不同的数字,代表的确实就是不同的对象,HashSet中就无法给这些不相同的对象去重。

   6、重写hashCode,让所有的Person对象都生成相同的哈希值0(相同的对象,必须有相同的哈希值,不同的对象,可以有相同的哈希值),相同的数字,无法表示就是相同的对象。

   7、重写完hashCode之后,发现再次运行测试类,去重成功,equals方法被调用

HashSet保证元素唯一性原理的总结

某个对象obj,在即将要存储到HashSet集合的时候,首先计算obj的hashCode值

在集合中的所有元素的哈希值,都和obj的哈希值不同,说明在集合中不存在obj,可以直接将obj存储到HashSet中

在集合中有若干元素的哈希值,和obj的哈希值相同,并不能说明obj已经存在于集合中,需要使用equals判断obj是否和那些与自己哈希值相同的元素是否相等

如果这些元素所有的和obj比较equals之后,都不相等,那么就说明obj不存在于集合中,可以将obj存储到HashSet中

如果这些元素有任意一个和obj比较equals之后,发现相等,那么就说明obj已经存在于集合中,所以obj就不能存储,存储失败。

保证元素唯一性的操作

重写hashCode

   相同的对象,一定要有相同的哈希值

   不同的对象,尽量有不同的哈希值

   操作:根据对象的属性来生成哈希值

重写equals方法:

   比较的就是各个对象的属性值,是否全都相同

最终操作:

   使用快捷键,直接全部生成:alt + shift + s   h

LinkedHashSet

是HashSet的一个子类,和HashSet保证元素唯一的原理相同。

将每个元素在存储的时候,都记录了前后元素的地址

效果:

可以根据存储元素的顺序,将元素取出

应用:

   既需要保证元素的唯一,又需要保证原来的顺序,就可以考虑LinkedHashSet类型

Map

概述

Map:双列集合的顶层接口

Map:单词含义,地图,地图上的每个点,都表示了生活中的一个具体位置。地图的点和生活中的位置,有一个一一对应的关系,这种关系是通过穷举的方式来描述的。

Map:描述的就是一个数据(key)到另一个数据(value)的映射关系(对应关系)

   一个数据(key):就是有规律的,容易记忆的,简单的一个数据

   另一个数据(value):没有规律的,不容易记忆的,复杂的一个数据

   大多数都是根据key来寻找value

映射:对应关系

   表达式方式:y = x * x

   穷举罗列:{1=1, 2=4, 3=9, 4=16, 5=25}

java中Map就是使用的穷举罗列的方式来描述映射关系

Map的特点:

   Key(键)是唯一的,value(值)不是唯一的

   每个键都只能对应确定唯一的值

Map和Collection的区别:

   Map是双列集合,Collection是单列集合

   Map的键是唯一的,Collection中的Set子接口中的元素是唯一的

   Map的所有操作和算法都是针对键有效,Collection中的set子接口中的操作和算法针对元素有效

Map中的常用方法

增加键值对:put(K key, V value)

删除方法:

   根据给定的键,删除对应的键值对:remove(K key)

   清空集合:clear()

获取方法:

   获取集合的大小:size(),获取键值对对数

   根据给定的键,获取对应的值:V get(K key)  Map的对象.get(key键)

判断方法:

   判断集合中是否存在某个键:containsKey(Object obj)

   判断集合中是否存在某个值:containsValue(Object obj)

修改方法:

   根据给定的键,修改对应的值:put(K key, V value)

   如果在集合中已经存在key这个键,那么使用put方法,就是修改其对应的值;如果集合中不存在key这个键,那么使用put方法,就是在集合中增加了一个键值对

Map集合的第一种遍历思路【熟练掌握】

获取Map集合中的所有键,放到一个Set集合中,遍历该Set集合,获取到每一个键,根据键再来获取对应的值。【根据键获取值】

获取Map集合中的所有键

   Set<K> keySet()     Map的对象.keySet();

遍历Set集合的两种方法:

   迭代器

   增强for循环

拿到每个键之后,获取对应的值

   V get(K key)

特点:

   获取了所有的键的集合之后,仍然要依赖原来的Map集合

 

Map集合的第二种遍历思路【熟练掌握】

获取Map集合中的所有键值对对象(Entry),到Set集合中,遍历Set集合,拿到的是每个键值对对象(Entry),从这个对象中分别获取键和值。【根据键值对对象获取键和值】

根据Map集合获取所有的键值对对象,到一个Set集合中

   Set<Map.Entry<K, V>>  entrySet()

例:Set<Map.Entry<Student,String>> entry =map.entrySet();

遍历Set集合,两种遍历方式

   迭代器

   增强for循环

获取到某个键值对对象

   Entry是Map接口中的内部接口,访问的方式:Map.Entry

   Entry:入口、条目、登记记录

   Entry的常用方法:   

       getKey()获取当前键值对对象的键

       getValue()获取当前键值对对象的值

HashMap

就是Map集合使用哈希表的存储方式的一种实现类

HashMap存储的是jdk中提供的类型的键,就可以直接保证键的唯一性

HashMap中存储的键,是自定义类型,无法保证键的唯一性;原因:虽然都是张三、23,但是这些对象并不是相同的对象,这些对象的哈希值计算结果各不相同,就说明一定不是相同的对象,所以无法保证键的唯一。重写hashCode和equals方法

   说明:HashMap的键的唯一性和HashSet的元素的唯一性,保证方式都一样

HashMap和HashSet的关系:

   1、HashSet是由HashMap实现出来的,HashSet就是HashMap的键的那一列

   2、将HashMap中的值的那一列隐藏掉,就变成了HashSet

LinkedHashMap

是HashMap的一个子类

和HashMap的不同之处在于,具有可预知的迭代顺序,存储键值对的顺序和遍历集合时取出键值对的顺序一致。

HashMap和Hashtable的关系

HashMap和Hashtable都是用于存储键和值的对应关系,都是Map的实现类,都是使用哈希表的方式存储。

不同点:

   1、版本不同,Hashtable是jdk1.0版本出现的,HashMap是jdk1.2版本出现的

   2、线程安全性不同,Hashtable是线程安全的,HashMap是线程不安全的

   3、Hashtable不能存储null键null值,HashMap可以存储null键null值

   4、命名规则:Hashtable时期没有形成完全严格的命名要求

Collections工具类

int  binarySearch(List<E> list, E e ):在一个有升序顺序的List集合中,通过二分查找寻找元素e的索引

fill(List<E> list, E e):将list集合中的所有元素都填充为元素e

int frequency(Collection<E> c, E e):返回在集合c中的元素e的个数

max、min:获取集合的最大值或者最小值

replaceAll(List<E> list, E oldVal, E newVal):将集合list中的所有指定老元素oldVal都替换成新元素newVal

reverse(List<E> list):将参数集合list进行反转

shuffle(List<E> list):将list集合中的元素进行随机置换

swap(List<E> list, int a, int b):将a索引和b索引的元素进行交换

synchronizedXxx方法系列:将一个线程不安全的集合传入方法,返回一个线程安全的集合

异常

异常的概述

在java程序运行过程中,出现的不正常情况,出现的错误,称为异常

异常就是一个对象,描述那些不符合生活正常情况的异常情况,包含了这些情况的原因、类型、描述以及位置,这些内容都封装到异常对象中。

异常也是一种处理异常情况的机制,可以进行跳转、捕获以及结束程序

异常的体系

Throwable:可抛出的,是异常体系的顶层父类,其他的异常或者错误都是Throwable的子类类型,只有Throwable的体系类型,才可以使用异常的处理机制

Error:错误,是Throwable的子类,用于描述那些无法捕获和处理的错误情况,属于非常严重的错误,StackOverflowError

Exception:异常,是Throwable的子类,用于描述那些可以捕获和处理的例外情况,属于不太严重的错误,ArrayIndexOutOfBoundsException

RuntimeException:运行时异常,是Exception的特殊的子类,在编译阶段不做检查的一个异常

体系图:

   Throwable

       Error

       Exception

           RuntimeException(运行时异常)

          编译时异常(除了运行时异常,其他都是编译时异常)

在jvm中默认处理异常的机制

在代码的某个位置,出现了和正常情况不同的情况,就将异常情况封装到一个异常对象中。

将异常对象抛给调用该方法的方法

某个方法接收到底层方法抛上来的异常,也没有办法自己处理,继续向上抛出,最终抛给主方法,主方法也没有办法处理,抛给调用自己的jvm虚拟机

Jvm虚拟机是我们手动调用的,只能将异常对象的所有信息,通过错误流打印出来,结束jvm虚拟机

总结,jvm默认处理的方式:【一层一层向上抛,jvm接收到之后结束自己】

手动处理异常的方式

有两大类处理异常的方式:

   异常的声明:某个方法有编译时异常,编译就会无法通过,需要在异常所在的方法声明上,声明该方法可能出现的编译时异常

   异常的处理:出现异常之后,可以通过某些格式来捕获和处理异常,可以让程序在出现异常之后,继续运行。可以定义自己处理异常的逻辑。

捕获处理异常的代码格式:

   try...catch

   try...catch...finally

   try...finally(无法捕获处理异常)

异常捕获的第一种格式

格式:

   try {

       可能发生异常的代码

} catch(可能出现异常的类型 标识符) {

   这种异常出现之后的处理方式

}

try:试一试

   try语句块中,是可能会运行失败的代码,try语句是用于对异常进行检测

catch:抓住、捕获

   抓住try语句中出现的异常,并且定义异常处理的方式

   小括号中声明可能出现的异常类型,标识符就是一个引用,指向了将来出现的异常对象

   大括号中用于定义异常出现之后的处理方式:可以是记录日志、可以是反复运行、可能是把异常封装进新的异常,进行抛出

运行机制:

   1、运行try语句中的代码

   2、如果没有发生任何异常,那么不再运行catch块中的内容

   3、如果发生了catch中声明的异常,那么就会被捕获到这个异常,执行catch块中的内容(try中如果发生了异常,try中,该语句后面的代码都无法执行了,直接跳到catch中)

  4、如果发生了catch中没有声明的异常,那么就无法捕获该异常,该异常的处理就使用jvm的默认处理方式

5、举例:    

   try理解为女神的生活,当女神没有任何问题的时候,不会来找你这个catch块的,一旦出现问题之后,会来找catch块(相当于一个备胎)

异常捕获的第一种格式的多种异常情况

在一段代码中,可能出现多种异常(虽然一次运行只能出现一个异常,但是出现哪个异常我们是不清楚的),所以要准备多种异常情况的处理机制。

格式:

   try {

       可能出现异常的代码

} catch (异常类型1  异常对象名1) {

   异常类型1出现之后的处理办法

} catch (异常类型2  异常对象名2) {

   异常类型2出现之后的处理办法

}

....

} catch (异常类型n 异常对象名n) {

   异常类型n出现之后的处理办法

}

执行流程:

   1、执行try中的内容,如果没有异常,try...catch语句直接结束

   2、如果有异常,那么就在发生异常的代码位置直接跳转到catch块中,try中后面的代码就不再继续运行了

   3、继续匹配各个catch块中的异常类型,从上到下,一旦匹配到某个catch声明的异常类型,就直接执行该catch块的处理方式。处理完成之后,try...catch语句就直接结束了,不会再去匹配后面其他的catch块的异常类型

4、注意事项:   

   1、如果在各个catch块中,出现了子父类的异常类型,那么子类异常的catch块,必须在父类异常catch块的上面,因为从上到下匹配方式,如果父类的catch块在上面,下面的catch块就没有出现的意义了,无法到达的代码。

   2、在jdk1.7之后,可以对异常类型进行逻辑的或运算,使用|表示,多种异常类型,可以使用相同的处理方式:

       catch(异常类型1 | 异常类型2  异常对象名称) {

           异常类型1和2的共同处理方式

}

异常捕获的第二种格式

格式:

   try {

       可能发生异常的代码

   } catch (可能发生的异常类型  异常对象名称) {

       当前异常类型的处理方式

} finally {

   一定要执行的代码

}

finally:一定要执行的代码

   1、如果把某句代码放在try中,可能在这句话前面有异常,那么这句话就无法执行;如果把某句代码放在catch中,有可能try中没有异常,就无法执行这句话;如果把某句代码放在try...catch之后,可能有未捕获的异常,那么这句代码也无法执行。

   2、finally:也是一个代码块,在这个代码块中的代码,一定会执行,无论上面描述的哪种情况,都会执行。甚至在代码中有return语句,都会先执行finally中的代码。

   3、作用:一般使用关闭资源

异常处理的第三种格式

格式:

   try {

       可能发生异常的代码

} finally {

   一定要执行的代码

}

作用:

   1、第三种格式无法捕获和处理异常,一旦发生任何异常,仍然会按照默认的处理方式,一层一层向上抛出,到达jvm,结束虚拟机

   2、无论try中的语句是否发生异常,finally中的代码都一定有执行的机会

   3、如果有两句代码,都需要有执行的机会,不希望第一句的成功与否影响到第二句的执行机会,那么就把这两句代码分别放在try和finally中

编译时异常和运行时异常

区别:

   1、继承体系的区别:编译时异常是Exception以及Exception的子类类型(RuntimeException除外),运行时异常是RuntimeException以及RuntimeException的子类。

   2、编译时异常,在编译阶段要对此异常进行检测的异常,一旦有这种编译时异常,在编译阶段是不可能给通过的,只能通过异常处理的两种手段解决(异常的声明、异常的捕获和处理)编译失败问题;运行时异常在编译阶段不做检查的异常,在编译阶段即使有了运行时异常,也不会编译失败,相当于在编译阶段没有异常,可以通过异常处理的手段处理,也可以使用异常避免的方式,来避免异常的发生。

继承体系中的常用方法

发现在异常的继承体系中,所有的方法定义在了Throwable这个顶层父类中,子类中几乎没有什么特有方法

Throwable中的构造方法:

   Throwable():创建一个没有任何参数的异常对象

   Throwable(String message):创建一个带有指定消息的异常对象

   Throwable(Throwable cause):创建一个有原因异常的异常对象

常用成员方法:

   getCause():获取异常对象中的原因异常,也就是当前异常对象的一个成员变量

   getMessage():获取异常的详细信息

   toString():获取异常对象的详细信息

   printStackTrace():打印异常的调用栈轨迹(有关异常的方法调用路径)

throw关键字

throw:抛出,用于抛出一个异常对象

异常是一个对象,当程序运行到某种情况时,程序员认为这种情况和现实生活不符合,就把当前的对于情况的描述,封装到一个异常对象中,通过throw关键字将异常对象进行抛出。

作用:

   创建一个异常对象,使用throw关键字抛出,实现了程序的结束或者跳转

说明:

   如果抛出的是编译时异常,那么这个异常必须使用异常处理的方式处理,才能编译成功

   如果抛出的是运行时异常,在编译阶段就相当于没有异常,可以不处理这个异常

throws关键字

throws:抛出,用于声明一个异常类型

在某个方法中,有一些编译时异常,没有给出处理的方案,没有捕获这个异常,没有处理这个异常,就说明这个方法是一个有问题的方法。为了让调用者在调用时,可以考虑到处理这个异常,所必须在当前方法的声明上,声明这个异常。

声明格式:

   修饰符 返回值类型 方法名称(参数列表) throws 异常类型1, 异常类型2,... {

       可能出现异常的代码

}

注意事项:

   1、如果抛出的是一个运行时异常,那么就相当于没有抛出异常,这种异常也不需要在方法上声明;声明了一个运行时异常,也相当于没有做任何声明

   2、如果抛出的是一个编译时异常,那么就必须进行声明或者捕获;如果声明了一个编译时异常,将来调用这个方法时,也相当于有一个声明的异常。

   3、在声明异常的时候,尽量声明小的异常、尽量声明少的异常

throw和throws的比较

throw是对异常对象的抛出,throws是对异常类型的声明

throw是对异常对象实实在在的抛出,一旦使用了throw关键字,就一定有一个异常对象出现;throws是对可能出现的异常类型的声明,即使声明了一些异常类型,在这个方法中,也可以不出现任何异常。

throw后面只能跟一个异常对象;throws可以跟很多个异常类型

自定义异常

jdk中提供了很多的异常类型,其中的绝大部分都没有自己特有的方法

定义这么多异常,没有特有方法、没有特有属性,原因:

   1、如果有了很多的异常类型的名字,将来在不同的异常情况下,就可以使用不同的异常类型创建对象。一旦发生了异常,可以很容易通过异常的类名来判断到底发生了什么异常情况

   2、如果有了很多的异常类型,不同的异常类型,就可以有不同的处理方式

3、在自己的业务中,jdk提供的各种异常,都无法描述当前的异常情况,就需要我们自己定义异常类型,用在自己的项目的业务中。

4、自定义异常的步骤:  

   1、定义一个类,以Exception结尾,IllegleAgeException,表示这是一个非法年龄异常

   2、让自己定义的这个类,继承一个Exception或者是RuntimeException        

       如果定义的是编译时异常,就使用Exception

       如果定义的是运行时异常,就使用RuntimeException

   3、构造方法不能被继承,需要手动添加

File类

概述

File类型:用于表示一个文件或者是文件夹的路径的对象(抽象路径名)

路径:用于描述文件或者文件夹的所在位置的字符串

路径分类:绝对路径、相对路径

绝对路径:从根目录开始的路径,称为绝对路径

   Windows系统中:盘符目录就是根目录,D:、E:

   Linux系统中:/就是根目录,从/开始的路径就是绝对路径

相对路径:相对于某个路径而言的路径

   相对于不同的路径,同样的相对路径,表达的是不同的路径

   Dos命令行中:相对于当前路径(Dos命令行所在的路径),a/b/c.txt

   Eclipse中:相对于当前工程的根目录

File类型的构造方法

File(String path):把字符串的路径,封装成一个File对象

File(String parent, String child):将父级路径和子级路径封装成一个File对象,其实描述的是父级路径和子级路径拼接后的路径

File(File parent, String child):将父级File对象路径和字节路径封装成一个File对象,其实描述的也是父级路径和子级路径拼接后的路径

File类型的创建方法

boolean createNewFile():创建当前File对象所描述的路径的文件

boolean mkdir():创建当前File对象所描述的路径的文件夹(如果父级路径不存在,那么不会自动创建父级路径)

boolean mkdirs():创建当前File对象所描述的路径的文件夹(如果父级路径不存在,那么自动创建父级路径)

File类型的删除方法

delete():删除调用者描述的文件或者文件夹

注意事项:

   1、delete在删除文件夹的时候,只能删除空文件夹

   2、delete方法不走回收站

File类型的重命名功能

renameTo(File dest)

   调用者是当前的文件或者文件夹的路径对象

   参数是变化之后的文件或者文件夹的路径对象

注意事项:

   如果在同一个文件夹下,修改路径,就是重命名

   如果在不同文件夹下,修改路径,就是剪切

File类型常用的判断功能

exists():判断当前调用者File对象,是否存在

isFile():判断当前调用者File对象,是否是文件

isDirectory():判断当前调用者File对象,是否是文件夹

File类型的获取功能

getAbsolutePath():获取当前File对象的绝对路径

getPath():获取的就是在构造方法中封装的路径

getName():获取最底层的简单的文件或者文件夹名称(不包含所造目录的路径)

length():获取文件的字节个数

   该方法被文件File对象调用时,返回的是文件的字节个数

   该方法被文件夹File对象调用时,返回的数据是不确定的

String[] list():获取当前文件夹下的所有文件和文件夹的名称,到一个字符串数组中

File[] listFiles():获取当前文件夹下的所有文件和文件夹的File对象,到一个File对象数组中

递归

概述

递:传递、逐渐的、一个接一个;归:回去,回到简单原始的状态

有一种从大到小,从复杂到简单的传递过程

递归:方法自己调用自己,解决的是一个比较复杂的问题,复杂的问题基于简单的问题已经解决的基础上,经过非常少的步骤,就可以解决这个复杂的问题。方法在自己调用自己的过程中,调用的逻辑总体不变,但是解决的问题的规模越来越小(调用者的规模越来越小,参数的规模越来越小),一直小到不需要递归的方式,也能容易解决这个问题为止,就不需要再递归调用该方法了。

递归代码的特点:

   1、自己调用自己:总体逻辑不变,但是规模在缩小;由于自己调用自己,所以功能都要单独的定义成一个方法

   2、代码会有两个分支:自己调用自己的分支,规模还比较大的时候,无法直接简单解决的时候;不需要自己调用自己的分支,规模已经简单到不需要自己调用自己,而使用一个很简单的操作就可以完成

   3、每次递归调用的时候,朝着规模减小的方向、朝着不需要递归的方向调用

递归的注意事项

递归的效率比较低,因为方法需要不断的进栈和出栈

递归的算法比较浪费栈内存的空间,栈内存本身比较小,很容将栈内存的内存空间耗尽,那么程序就无法继续执行下去,报出一个错误:

   StackOverflowError:栈内存溢出

国外有一个网站:StackOverflow,专业版的知乎

递归算法的好处:

   思路比较简单,假设简单问题解决了

所有的递归算法,都有非递归的解决方式,只不过非递归的算法设计比较困难

IO概述

IO:input和output两个单词的缩写,input是输入,output是输出

   站在内存的角度看待方向,从其他设备进入内存的,都是输入,从内存到其他设备的,都是输出

   I/O就是用于设备之间进行数据交互的对象所属的类型

java中操作设备设备之间数据传输的对象,都是IO流对象,这些对象所属的类型,都在io包中。

 

IO分类

分类的方式有两种:按照功能可以分类,按照流向也可以分类

按照功能分类:

   字节流:可以直接操作字节的流对象

   字符流根据分类,:可以直接操作字符的流对象

按照流向分类:

   输入流:其他设备流到内存的流对象

   输出流:内存流到其他设备的流对象

IO流的体系结构,有四种流对象的类型

   字节流:

       字节输入流:InputStream

       字节输出流:OutputStream

  字符流:

       字符输入流:Reader

       字符输出流:Writer

IO程序书写流程

在操作之前,要导包,io包

在操作流对象的时候,要处理解决异常

在操作完流对象之后,需要关闭资源

字节流、、、、、、、、、、、、、、、、

可以直接操作字节信息的流对象

根据流向,可以分成字节输入流和字节输出流

顶层抽象父类分别是:InputStream 和 OutputStream

根据交互设备的不同,有不同的具体子类

InputStream

字节输入流的顶层抽象父类

常用的方法

   read():从当前的字节输入流中,获取一个字节

   read(byte[] arr):将arr.length个字节,读取到arr中

InputStream是一个抽象类,不能直接创建对象,只能由子类创建对象

FileInputStream

InputStream的一个具体子类,用于和磁盘上的文件进行交互

FileInputStream不仅可以一次读取一个字节,也可以一次读取很多个字节;不仅可以读取纯文本文件,也可以读取图片、视频、音频等非纯文本文件。一切数据在计算机中都是以字节的形式在存储和计算

构造方法:

   FileInputStream(File f):将File对象封装成字节输入流,将来可以读取这个文件中的信息

   FileInputStream(String path):将字符串封装成字节输入流,将来可以读取信息

  

   注意事项:无论是哪个构造方法,都只能封装文件的路径,封装文件夹的路径没有任何意义,因为文件夹本身没有任何数据,所以也不能使用流对象读取数据。

两个常用方法:

   read():一次读取一个字节,返回值类型是int类型,原因要把返回的字节前面加上24个0,无论读取到的是负数还是整数,都会变成正数,只要是从文件中读取到的数据,都是整数;如果返回了-1,就说明不是从文件中读到的数据,而是整个方法专门准备的文件末尾的标记。说明:虽然每次调用该方法,但是返回的内容却不会相同,因为文件指针在不断向后移动。

   read(byte[] arr):一次读取多个字节,存储到字节数组中

OutputStream

字节输出流的顶层抽象父类

常用方法:

   write(int b):将一个字节信息写出到指定的设备中

   write(byte[] arr):将一个字节数组中的所有信息,写出到指定设备中

   write(byte[] arr, int offset, int len):将一个字节数组arr中的从offset索引开始,总共len个字节写出到指定的设备中。

这个类是抽象类,不能直接创建对象

FileOutputStream

说明:可以将字节数据写出到指定的文件中

构造方法:

   FileOutputStream(File f):将f描述的路径文件封装成字节输出流对象

   FileOutputStream(String path):将path描述的文件路径封装成字节输出流对象

   使用字节输出流写出数据,就进入到关联的文件中。

注意事项:

   1、成员方法都来自于抽象父类

   2、写到磁盘上,存储的就是数字,既没有编码,也没有解码;如果使用文本编辑器读取这个文件,就会发生两步:先读取数据,通过编码表进行解码,让我们看到字符

文件拷贝效率提升

使用一个字节一个字节拷贝的方式,效率非常低:IO的次数过多,有多少个字节,就要IO两倍的次数

提升的思路:一次多读一些数据,一次多写出一些数据,使用FileInputStream中的read(byte[] arr)和FileOutputStream中的write(byte[] arr)。数组的大小可以准备成和文件一样样大。配合InputStream中的available方法可以获取源文件的字节个数,用于创建数组的大小。

数组属于内存的,内存的大小是有限的,如果文件过大,就无法创建大小相同的数组。

只能考虑使用小一些的数组,每次拷贝源文件的一部分,多拷贝几次。涉及的方法:

   InputStream中的read(byte[] arr):将数据读取到数组中,返回本次读到的有效字节的个数,如果返回值为-1,表示本次读到的有效字节个数为0,表示到达了文件末尾

   OutputStream中的write(byte[] arr):将数组中的所有数据,都写出到了目标文件中

   OutputStream中的write(byte[] arr, int offset, int len):将数组中的指定部分的数据,写出到目标文件中(在读取的时候,读到了多少有效字节,就将这么多有效字节写出到目标文件中),一般offset都选择0

注意事项:最终拷贝方案就是小数组

   1、数组的大小可以任意选择,数组越大,拷贝次数越少,拷贝的效率越高

   2、一般情况,数组的大小使用1024的整数倍,在jdk中喜欢使用1024*8大小作为缓冲区的大小。

InputStream类型中read()方法和read(byte[] arr)方法的比较

读取字节个数的区别:

   read()方法一次只能读取一个字节

   read(byte[] arr)方法一次可以读取多个字节,取决于数组的大小

读取到的字节信息存储的位置不同:

   read()读取到的文件字节信息是作为返回值进行返回的

   read(byte[] arr)读取到的字节信息,存储到参数数组arr中

返回值的含义不同:两个方法的返回值类型都是int类型

   read()方法返回的是读取的有效字节本身

   read(byte[] arr)方法的返回是读取到的有效字节的个数(返回值的取值范围:-1,1~arr.length)

高效缓冲流

BufferedInputStream和BufferedOutputStream

是包装类型:本身不具备读写的功能,只是在某个具体的流对象的基础上,对其进行加强,例如FileInputStream和FileOutputStream,原本效率较低,加强之后,就效率较高

构造方法:

   BufferedInputStream(InputStream is):将指定的具体的字节输入流传入构造方法的参数,形成一个高效版本的字节输入流

   BufferedOutputStream(OutputStream os):将指定的具体的字节输出流传入构造方法的参数,形成一个高效版本的字节输出流

使用:

   这两个高效流还是字节流,还是InputStream和OutputStream,所以抽象父类中的那些方法仍然可以继续使用

原理:

   1、BufferedInputStream高效的原理:在该类型中准备了一个数组,存储字节信息,当外界调用read()方法想获取一个字节的时候,该对象从文件中一次性读取了8192个字节到数组中,只返回了第一个字节给调用者。将来调用者再次调用read方法时,当前对象就不需要再次访问磁盘,只需要从数组中取出一个字节返回给调用者即可,由于读取的是数组,所以速度非常快。当8192个字节全都读取完成之后,再需要读取一个字节,就得让该对象到文件中读取下一个8192个字节了。

   2、BufferedOutputStream高效的原理:在该类型中准备了一个数组,存储字节信息,当外界调用write方法想写出一个字节的时候,该对象直接将这个字节存储到了自己的数组中,而不刷新到文件中。一直到该数组所有8192个位置全都占满,该对象才把这个数组中的所有数据一次性写出到目标文件中。如果最后一次循环过程中,没有将数组写满,最终在关闭流对象的时候,也会将该数组中的数据刷新到文件中(也可以直接刷新.flash获取最后的数组)。

输出流中的close()和flush()的区别

close方法会先调用flush方法

close方法用于流对象的关闭,一旦调用了close方法,那么这个流对象就不能继续使用了

flush只是将缓冲区中的数据,刷新到相应文件中,而不会将流对象关闭,可以继续使用这个流对象。但是如果flush方法使用过于频繁,那么丧失了缓冲区的作用。

字符流、、、、、、、、、、、、、、、

使用字节流处理字符的问题

使用字节流写字符

   可以使用,但是需要先把字符串转成字节数组,再存储到文件中,比较麻烦

使用字节流读取字符

   如果是纯英文,可以一次读取一个字节

   如果是纯中文,可以一次读取两个字节(GBK)

   如果是中英文混杂,每次不知道读取多少个字节,因此无论字节数组准备多大,都会出现乱码

解决方案

出现乱码的原因:

   每次不知道读取多少个字节,转换成字符。

   而我们在代码中将每次读取的字节个数写死了。

解决:

   动态判断每次应该读取多少个字节

   在GBK编码表中,如果是一个英文,那么一定读取到的字节是正数,如果读取到的字节是正数,那么就可以断定是英文字符,就读取一个字节转成字符即可

   在GBK编码表中,如果是一个中文,那么就一定读取到的第一个字节是负数,如果读取到一个负数,就说明读到的是一个中文,就需要再次读取一个字节,两个字节一起转成字符。

说明:

   如果在工程中频繁做这么底层的操作,太复杂,直接使用jdk中已经提供好的解决方案

   字符流:不仅能动态判断GBK的每个字符是中文还是英文,还可以判断其他编码表的所有语种

字符流的使用

抽象顶层父类:Reader、Writer

常用方法:

   Reader:

       read():读取一个字符,返回值如果是-1,表示到达文件的末尾

       read(char[] arr):将一堆字符存储到一个字符数组中

   Writer:

       write(int c):写出一个字符

       write(String str):写出一个字符串

       write(char[] arr):将一个字符数组的所有内容写出

       write(char[] arr, int offset, int len):将字符数组的一部分写出到目标中

           arr:字符存储的容器

           offset:从哪个位置开始

           len:写出多少个

抽象父类不能直接创建对象,需要使用具体的之类类型来创建

使用FileReader和FileWriter可以作为创建对象的类型

字符流的拷贝

使用字符输入流读取信息,使用字符输出流写出信息,完成文件的使用字符流拷贝

字符流拷贝的必要性:

   没有必要使用字符流来进行拷贝,因为字符流会先将字节信息转成字符,读取到内存中,最后还要再把字符信息转成字节信息,写出到另外一个文件中。中间做了两次没有意义的相反的操作,浪费了时间。

字符流的使用场景:

   如果在读取到字符之后,需要人为的阅读和修改这个字符,那么就需要使用字符流

   如果只是简单的将信息进行转移或者拷贝,就不需要转成字符。

字符流是否可以操作非纯文本文件

非纯文本文件:文件中可以储存非字符以外的内容,例如图片、视频、音频等不能使用字符表示的信息。

结论:不能

原因:

当字符流读取到一个字节信息之后,需要查询编码表转成字符信息,如果是非纯文本文件,就可能读取到的字节信息无法转成对应的字符,因为这个字节信息在编码表中没有对应的字符,就只能使用英文的?来代替这个字符,这一步中,就对信息进行篡改。接下来将篡改之后的?字符信息,通过编码表转成字节信息(这一步虽然正确,但是已经没有意义),转成的字节信息,就和当初字符输入流读取的字节信息不一致了,写出到目标文件,就是错误信息

字符流使用小数组拷贝

使用FileReader中的read(char[] arr)方法,将多个字符读取到数组中,返回的值表示本次读取到的有效字符的个数,如果返回-1表示读取的有效字符个数为0,到达了文件的末尾

使用FileWriter中的write(char[] arr, int offset, int len)方法,将数组arr中的一部分字符(从offset索引开始的len个)写出到目标文件中。

说明:

   不使用小数组进行拷贝,发现效率也比较高,因为在FileWriter中就有一个缓冲区数组,所以写出的效率就比较高。

   在我们使用小数组之后,还可以提升读取字符的速度,最终也可以提升拷贝效率

高效缓冲字符流

BufferedReader和BufferedWriter

使用:

   创建了高效缓冲流对象之后,使用和加强之前一样的方式,BufferedWriter是Writer的子类,BufferedReader是Reader的子类,所以可以继续使用在抽象类中定义的各种方法。

高效的原因:

   BufferedReader:每次调用read方法,只有第一次从磁盘中读取了8192个字符,存储到该类型对象的缓冲区数组中,将其中一个返回给调用者,再次调用read方法时,就不需要再访问磁盘,直接从缓冲区中拿一个出来即可,效率提升了很多。

   BufferedWriter:每次调用write方法,不会直接将字符刷新到文件中,而是存储到字符数组中,等字符数组写满了,才一次性刷新到文件中,减少了和磁盘交互的次数,提升了效率

高效缓冲字符流的特有方法

BufferedReader:

   readLine():可以从输入流中,一次读取一行数据,返回一个字符串,如果到达文件末尾,则返回null

BufferedWriter:

newLine():换行。在不同的操作系统中,换行符各不相同,newLine方法就是可以给我们根据操作系统的不同,提供不同的换行符

包装方式的灵活性

System.in:标准输入流,默认关联到键盘

通过转换流InputStreamReader可以将System.in包装成字符流

通过BufferedReader将InputStreamReader包装成带缓冲的字符流,可以调用readLine方法

转换流

编码表

GBK:国标码,定义的是英文字符和中文字符。在GBK编码表中,英文字符占一个字节,中文字符占两个字节。

UTF-8:万国码,定义了全球所有语言的所有符号,定义了这些符号和数字的对应关系,英文字符使用一个字节进行存储,中文字符使用三个字节进行存储

转换流

OutputStreamWriter:字符流到字节流的桥梁,可以指定编码形式

  构造方法:OutputStreamWriter(OutputStream os, String charSetName)

   创建一个转换流对象,可以把将来方法中接收到的字符,通过指定的编码表charSetName,编码成字节信息,再通过指定的字节流os,将字节信息写出

   使用:直接使用Writer中的方法即可(该类是Writer的子类)

InputStreamReader:字节流到字符流的桥梁,可以指定编码形式

   构造方法:InputStreamReader(InputStream is, String charSetName)

   创建一个转换流对象,可以使用is这个指定的字节流,从磁盘中读取字节信息,通过指定的编码表charSetName,将字节信息解码成字符信息,返回给调用者

   使用:直接使用Reader类中的方法即可(该类是Reader的子类)

说明:

   无论是读取的时候,还是写出的时候,都需要参考读取文件和目标文件的编码形式

   读取源文件时,解码的形式必须和源文件的编码形式一致

   写出到目标文件时,编码形式必须和目标文件的编码形式一致

内存输出流

概述

名字是输出流,体系也是在IO的体系中,但是其实没有内存和其他设备的交互,仅仅是在内存中做了拷贝。所以没有真正的IO

类名:ByteArrayOutputStream

作用:当从某个文件汇总,读取了一部分不完整的字节信息的时候,需要找个一个容器缓存这些不完整的信息,ByteArrayOutputStream找个类型就可以充当找个容器,因为这个容器可以自动增长。

本质:一个可以自动增长的字节数组,并且提供了一些实用的方法

常用方法:

   toByteArray():获取对象中的字节信息,返回一个字节数组

   toString():将对象中的字节信息,进行解码,返回一个解码后的字符串

对象操作流

概述

用于将内存中的对象,直接写出到目标文件中,或者从文件中读取一个对象到内存中。

对象输出流、对象输入流

对象输出流

ObjectOutputStream:也是一个字节输出流,是OutputStream的子类,可以输出一个对象,用于将内存中的对象,写到其他设备中,例如文件、网络

也是一个包装类,可以将一个没有输出对象功能的字节输出流,包装成有输出对象功能的字节流,包装完成之后,就多了一些方法。

构造方法:

   ObjectOutputStream(OutputStream os):将一个普通的字节输出流包装成对象输出流

最重要的成员方法:

   writeObject(Object obj):将内存中的obj对象,输出到该对象流中

注意事项:

   1、要存储到文件中的对象,所属类型必须实现java.io.Serializable接口

   2、对象存储到文件时,没有经过编码也没有经过解码,直接将字节信息存储到文件中;使用文本编辑器打开这个文件,相当于是做了解码操作,所以一定是乱码。将来这个文件就不应该使用文本编辑器打开,也不应该给人阅读,而是用于将来使用输入流读取的内容

对象输入流

ObjectInputStream:也是一个字节输入流,也是一个包装类,拥有父类的所有方法

作用:是InputStream的子类,可以将文件中、网络中的一个对象,读取到内存中,进行使用

构造方法:

   ObjectInputStream(InputStream is):将一个普通的字节输入流,包装成对象流

有一个特有方法:

   readObject():可以将流中的一个对象读取到内存中

注意事项

序列化和反序列化概念:

   对象从内存到其他设备的过程:序列化

   对象从其他设备到内存的过程:反序列化

序列化还是反序列化:都需要实现接口java.io.Serializable

异常分析:

   EOFException,End Of File Exception,文件末尾异常   

   当文件已经到达末尾的时候,仍然要使用对象输入流读取对象,就发生此异常

避免方式:

   在写出对象的时候,不要直接将对象写出到文件中

   而是,先把所有对象装进一个集合中,将集合整体写到文件中(也就是只把一个对象写到了文件中)

   将来读取的时候,只读取一个集合对象,在内存中遍历这个集合,获取每个元素

版本号问题:

   Exception in thread "main" java.io.InvalidClassException: com.ujiuye.demos.Student; local class incompatible: stream classdesc serialVersionUID = 384528848059540731, local class serialVersionUID = 2430787247088292168

     意思:修改了本地的Student类型,新的类型和文件中对象的类型已经不一致,所以两个版本冲突了,标志就是两个版本的序列化版本id不同

     解决:不要使用自动生成的序列化版本ID,手动给定一个序列化版本ID,将来这个类型是否发生了版本变化,主要取决于程序员是否手动修改了这个类型的版本ID,如果修改了,那么文件中的对象类型和本地类型就不兼容,如果没有修改这个版本ID,那么无论怎样修改了类型内容,都可以做到文件对象类型和本地类型兼容。

随机访问流

使用类型:RandomAccessFile

不是一个流对象,不在IO体系中,但是比流对象更加的强大;既可以读,也可以写;可以随机访问。

随机访问:人的需求是随机的,可能一会儿想访问这个位置,一会儿要访问另外一个位置,而随机访问流可以支持人的随机访问的需求。

构造方法:

   RandomAccessFile(String path, String mode)

   path:文件的路径

   mode:操作文件的模式, “r”是只读,”rw”是读写

常用成员方法:

read():读取一个字节

read(byte[] arr):读取若干字节,存储到arr中

write(int b):写出一个字节到文件中

write(byte[] arr):将一个字节数组的内容写出

write(byte[] arr, int offset, int len):将字节数组的一部分写出

readLine():一次读取一行

readXxx:可以将基本类型直接从文件中读取

writeXxx:可以将基本类型直接以字符串的形式写入文件中

seek(long pos):用于设定光标的位置,从指定位置开始读写

打印流

概述

打印流:打印字节流和打印字符流(PrintStream和PrintWriter)

特点:

   提供了很多的print和println的方法,可以很方便的将各种数据类型以字符串的形式进行输出(会将所有数据类型,先转成字符串,然后进行输出)

说明:

   PrintStream是一个打印字节流、PrintWriter是打印字符流,字节流和字符流本身的方法,他们都有,但是我们使用这两个流对象,并不关注这些普通的写出字节和写出字符的方法,更关注的是这两个类型中的print和println的方法,而对于这两个方法,PrintStream和PrintWriter没有太多区别的。

PrintStream

打印字节流:System.out就是这个类型的流对象

在该类型中,提供了很多的print和println的方法,可以将各种数据类型转换成字符串进行输出

注意事项:

   1、如果直接使用System.out的方式来获取PrintStream的对象,这个对象默认关联的输出目的地就是控制台;如果手动创建PrintStream的对象,那么关联的就是指定的设备。

   2、PrintStream中重载了很多print和println的方法,有两个比较特殊,print(char[] arr)、print(Object obj)。一般的数组使用print方法,会直接打印数组的地址,唯独字符数组打印之后出现的是数组的内容。普通的数组走的都是println(Object obj)方法,先获取了数组的toString的内容,也就是数组的地址,而字符数组走的是println(char[] arr)方法,没有运行String.valueOf方法,没有获取数组的toString内容,而是直接调用了write方法用于输出字符内容

4、构造方法:   

   PrintStream(File f)

   PrintStream(String path)

   PrintStream(OutputStream os):将一个普通的字节输出流,包装成打印流,就可以使用print和println等方法了

PrintWriter

和PrintStream一样,也是有很多的print和println的方法,其中打印的方法也基本相同

PrintWriter是一个字符流,是Writer的子类,就有一个缓冲区,就涉及到一个刷新的功能。

PrintWriter提供了自动刷新的功能

使用自动刷新的方式:

   1、选择可以提供自动刷新功能的构造方法:PrintWriter(OutputStream os, boolean autoFlush),可以将一个普通的字节输出流,包装成可以自动刷新的打印流;PrintWriter(Writer w, boolean aotuFlush),可以将一个普通的字符输出流,包装成可以自动刷新的打印流。

   2、调用PrintWriter中具有自动刷新功能的方法。(println、printf、format)

标准输入输出流

标准输入流:System.in

   类型:InputStream类

   分类:字节输入流

   设备:默认关联键盘

   包装:使用转换流可以包装成字符流、使用BufferedReader可以包装成缓冲流

   对标准输入流关联的设备进行修改:setIn(InputStream is)

标准输出流:System.out

   类型:PrintStream类

   分类:打印字节流

   设备:默认关联到控制台

   修改:setOut(PrintStream ps)

Properties

概述

Properties对象表示一个持久的属性集

   属性集:属性名称和属性值的对应关系,其实还是一个双列集合

   持久的:可以保存到流中,也可以从流中读取。可以很方便和文件进行交互

看到Properties没有泛型,因为表示一个配置文件中的信息,而配置文件中都是字符串,所以Properties类型不需要使用广泛的类型,存储的键和值都是字符串类型

Properties是Hashtable的子类,所以可以当做普通的Map来使用

Properties中的特有方法

getProperty(String propertyName):根据一个字符串类型的属性名称,获取一个对应的属性值

setProperties(String propertyName, String propertyValue):将一个属性名和对应的属性值添加到当前对象中

stringPropertyNames():获取当前属性集对象中的所有属性名称的Set集合

Properties中和配置文件交互的方式

 load(InputStream is)

       从流is中,加载配置文件的信息,到内存的Properties对象中,形成双列集合

   load(Reader r)

       从流r中,加载配置文件的信息,到内存的Properties对象中,形成双列集合

   说明:无论是is和r,关联的都是对应的配置文件

 store(OutputStream os)

       将内存中的Properties对象中键值对信息,写出到流os中

   store(Writer w)

       将内存中的Properties对象中键值对信息,写出到流w中

   说明:无论是os还是w,关联的都是对应的配置文件

注意事项:

   1、使用prop修改了属性值之后,文件中的属性值不会发生变化,因为prop只修改了内存中的对象数据,没有同步到文件中。必须调用store方法之后,才能将变化同步到文件。

   2、调用store方法时,一般需要指定更新属性的原因,即第二个参数comments,如果没有注释,可以传入null;如果有注释,必须是纯英文注释。

IO中保证流对象关闭的标准格式

捕获异常和声明异常的原则

如果知道如何处理异常,那么就使用try...catch来进行处理;如果不知道如何处理异常,那么就使用throws来对异常进行声明,或者将当前的异常包装成其他的异常类型,进行声明,因为越到代码的高层,拥有更多的资源、更高的位置和权力,知道如何处理,底层被调用的方法,虽然是出现异常的位置,但是并不知道如何处理。

如果你希望程序出现异常之后,继续运行下去,那么就使用try...catch;如果出现异常之后,希望当前方法的代码停止运行,那么就使用throws

IO中保证流对象关闭的格式(jdk1.7之前)

将流对象的创建使用 与 流对象的关闭,分离开,分别放在try和finally中(流对象的创建和使用的异常,不会影响流对象的关闭的执行)

为了让try和finally中都可以使用流对象的引用,所以将两个引用在外面声明,扩大访问范围,在try和finally中使用

为了保证两个引用有初始化值,所以给两个引用分别赋值null

为了避免两个流对象在关闭的时候出现空指针异常,所以在关流之前判断是否为null

为了让第一个流的关闭失败,不要影响第二个流关闭的机会,所以将两个流的关闭,分别放在了嵌套的try...finally中

IO中保证流对象关闭的格式(jdk1.7之后)

格式:

   try (

          流对象的创建

) {

   流对象的使用

}

说明:

   流对象使用之后,不需要手动关闭,因为这个格式已经帮助我们自动关闭了流对象

单例设计模式的作用及用途

单例类的作用:让一个类只能创建一个实例(因为频繁的创建对象,回收对象会造成系统性能下降。)。解决对象的唯一性,保证了内存中一个对象是唯一的 。

使用单例类条件:当前对象的创建比较消耗资源,我们在使用这个对象时只需要有一个就可以应用。这个时候就可以将其设计成单例的模式。在一定的场景中,只有一个这样的实例,比如说银行的号码管理器等。java连接数据库,与数据库的连接会比较消耗资源。我们可以将其做成单例的, 这个时候在内存中就有一个,所有人操作的都是一个。

单例模式的步骤:

1.构造方法私有化
       让外部不可以直接创建本类对象
2.在类内实例化一个本类对象 ,属性一般都会private
      必须在内部得到一个本类对象。是可以让外部访问到的。但是属性一般都要私有化
3.在本类内提供一个外部可以访问的获得本类对象的static方法。
      提供一个可以被外部访问的方法,这个方法构造方法私有化了,所以要做在类方法。

 

 

饿汉式(工作中)
                class Single
                {
                        //在本类内创建一个对象.
                        private static Single s=new Single();
                        //私有构造方法
                        private Single(){
                        
                        }
                        //对外提供公共的可以获得本类对象的方法.
                        public static Single getInstance(){
                                return s;
                        }
                }

 

 

 

 

 

 

 

 

 

 

//延迟加载的方式(懒汉式)  不完善,讲多线程还要对其进行改进.
                class Single
                {
                        //在本类内创建一个对象.
                        private static Single s;
                        //私有构造方法
                        private Single(){
                        
                        }
                        //对外提供公共的可以获得本类对象的方法.
                        public static Single getInstance(){
                                if(s==null){
                                        s=new Single();
                                }
                                return s;
                        }
                }

 

 

 

 

 

 

 

 

 

 

 

 

public class 调用方式 {

   public static void main(String[] args) {

       SingleLazy sl = Singl. getInstance ();

       System.out.println(sl);

   }

}

 

 

 

原文地址:https://www.cnblogs.com/zhaojiu/p/14020737.html