1.1.内部类介绍
1.1.1. 内部类引入
生活中经常会有一个事物再另一个事物里面,二者相互依赖的情况;
例如:人体里面包含有心脏,二者是相互依赖的;
使用java程序描述人和心脏:
原来的描述方法:
问题:描述人和心脏的两个类是两个相互独立的类,可以单独使用;体现不出来二者相互依赖的关系;
要解决这个问题,就要使用内部类;
2.png
1.1.2. 内部类的代码体现
内部类,就是定义在其他类里面的类;
根据类写在其他类里面的位置的不同,可以分为成员内部类和局部内部类;
而包含内部类的类就叫做外部类;
1.2.成员内部类
成员内部类作为外部类的一个成员,也可以使用成员的修饰符修饰;
1.2.1. 非静态成员内部类
没有使用static关键字修饰的成员内部类就是非静态成员内部类;
类的非静态成员必须通过类的对象使用;
所以非静态成员内部类,也和外部类的对象有关,里面只能定义非静态成员;而且非静态成员内部类的对象,必须依赖于外部类的对象存在;
static final int b = 20;
结论:再非静态成员内部类中,只能定义非静态成员和常量变量,不能定义静态成员;
4.png 5.png
结论:在外部类以外的类中使用非静态成员内部类,必须先要创建外部类的对象,然后通过外部类的对象创建内部类的对象,通过内部类的对象使用内部类的成员;
6.png 7.png
结论:在外部类中使用内部类的成员,外部类的非静态函数中可以直接创建内部类的对象;静态函数中需要先创建外部类对象,然后再通过外部类对象创建内部类对象;
要能够执行到非静态成员内部类里面,肯定已经存在内部类的对象;
一旦存在内部类的对象,肯定已经存在外部类的对象;
8.png
非静态成员内部类里面可以直接使用外部类的所有成员;
9.png
结论:再非静态成员内部类中可以直接使用外部类的所有成员;如果内部类中定义了和外部类一样的成员,要使用外部类的成员,
需要通过 外部类类名.this.成员名 的方式使用;
如果是调用外部类的静态成员,还可以直接使用 外部类类名.成员名 的方式调用;
1.2.2. 静态成员内部类
使用static关键字修饰的成员内部类就是非静态成员内部类;
静态成员和类的对象无关,可以直接通过类名使用;
所以静态成员内部类和外部类的对象也没有关系;只表示一个类在另一个类的里面,所以静态成员内部类又被称为嵌套类;
所以静态成员内部类使用起来和一般的类一样,里面可以定义任何成员;要使用它的静态成员,可以直接通过类名使用;非静态成员,必须通过对象使用;
10.png
在外部类以外的类中使用静态成员内部类:
11.png
在外部类中使用静态成员内部类的成员:
12.png
在外部类中可以直接使用内部类的私有成员;
13.png
结论:
静态成员内部类,只表示这个类的位置在外部类中,和外部类的对象无关,静态成员可以直接通过类名调用,非静态成员需要通过类的对象使用;
在静态成员内部类中使用外部类的成员,也是非静态成员通过外部类的对象访问,静态成员直接通过类名使用;
1.2.3. 私有成员内部类
使用private关键字修饰的成员内部类就叫做私有成员内部类;
私有成员内部类,只能在定义它的外部类里面使用;
14.png 15.png
1.2.4. 私有静态成员内部类的一个应用:单例的实现
单例,解决的就是在程序运行中,一个类最多只能有一个对象的问题;
饿汉式: 在类加载的时候就创建类的唯一实例
好处:可以保证对象的唯一性;
弊端:只要类一加载,就会创建对象,会造成内存上的浪费;
懒汉式: 在第一次调用类的对象时会创建这个类的唯一对象;
好处:只有在需要使用时才创建对象,避免内存浪费;
弊端:在多线程环境下,不能保证对象的唯一性;
使用私有静态内部类的方式,可以避免上述问题,同时拥有他们的好处;
16.png
原理:
在使用Single类的其他静态函数时,虽然也会加载Single类,但是因为没有使用到Inner类,所以不会加载Inner类,也就不会创建Single类的对象,避免了饿汉式的弊端;
在需要使用Single类的对象时,就会调用getInstance()函数,这个函数里面会使用到Inner类;在第一次调用这个函数时才会加载Inner类,在加载Inner类的时候才会创建Single类的对象;因为一个类只会加载一次,所以Single类的对象也只会创建一次,可以保证是唯一的,避免了懒汉式的弊端;
1.3.局部内部类
定义在外部类的局部位置的类就叫做局部内部类;
一般都定义在函数中;
局部主要指的是使用范围收到限制,所以局部内部类只能在定义它的那个函数中使用,其他地方不能使用;
17.png
局部内部类中可以书写的成员:
18.png
局部内部类的使用:
19.png
局部内部类使用外部类的成员:
20.png 21.png 22.png 23.png
1.4.匿名内部类
1.4.1. 匿名内部类
内部类也是一个类,也可以继承其他类,也可以实现接口;
24.png
有的时候,需要继承一个类,或者实现一个接口,应该定义一个类;但是如果这个类只需要使用一次,单独创建一个类就太麻烦,
此时就可以使用匿名内部类:
25.png 26.png
匿名内部类的写法:
new 类名():创建类的对象
new 类名(){}:表示创建类的子类的对象;这个类的子类是一个匿名类;
new 接口名(){}:表示创建了接口的一个实现类的对象;这个接口的实现类是一个匿名类;重写接口中的方法,应该写在大括号里面;
27.png
1.4.2. 匿名内部类的面试题分析
问题:下列程序有没有问题?如果有,问题是什么?如果没有,运行结果是什么?
class A
{
public static void main(String[] args)
{
new A(){
void show(){
System.out.println("AAAAAAAAAAA");
}
}.show();
A a = new A(){
void show(){
System.out.println("AAAAAAAAAAA");
}
};
a.show();
}
}
28.png
1.5.内部类小结
内部类的概念:定义在其他类里面的类叫做内部类,包含内部类的类叫做外部类;
内部类的作用:内部类主要是用来描述一个事物存在于另一个事物里面,依赖于外部事物存在的;
内部类的格式:
内部类的分类:
成员内部类
非静态成员内部类:
1、非静态成员内部类必须依赖于外部类的对象使用,所以:
A、非静态内部类中不能定义静态成员,除非是常量变量;
B、在外部类的非静态函数中可以直接创建内部类的对象;
C、在外部类的静态函数中和外部类以外的类中都不能直接创建内部类对象,需要先创建外部类对象,然后通过外部类对象创建内部类对象;
2、在内部类中,可以直接使用外部类的所有成员;
3、如果内部类中定义了和外部类中一样的成员,优先使用内部类中的;要使用外部类中的成员,可使用外部类类名.this.成员 的方式;
静态成员内部类:
1、静态内部类和外部类的对象无关,仅仅表示内部类和外部类的位置关系是嵌套关系,所以又被称为嵌套类;
2、静态成员内部类中可以书写所有成员;
3、外部类使用静态内部类,访问静态成员可以通过类名直接访问,访问非静态成员需要通过内部类对象访问
4、静态内部类可以直接使用外部类的静态成员;非静态成员,需要通过外部类对象使用;
5、外部类以外的类要使用静态内部类的成员,和使用一般类一样,只是需要在类名前面加上外部类类名,表示内部类是外部类的成员;
私有成员内部类:
成员内部类作为外部类的成员,也可以使用访问权限修饰符修饰,如果使用private修饰,成员内部类就成为私有成员内部类,只能在外部类里面使用,不能在外部类以外的类中使用;
局部内部类
1、定义在函数里面的类叫做局部内部类
2、在局部内部类中不能定义静态成员,除非是常量变量;
3、局部内部类只能在定义它的函数中使用,随着定义它的函数的执行而执行;
4、在非静态函数中的局部内部类,可以直接使用外部类的静态和非静态成员;
5、在静态函数中的局部内部类,就只能直接使用外部类的静态成员;要使用非静态成员,需要创建外部类的对象;
6、在局部内部类中要使用定义它的函数中的变量,需要将这个变量声明为final的;
匿名内部类:
当要使用一个类的子类(或接口的实现类)时,如果这个子类(或者实现类)只会使用一次,就没必要专门定义一个类出来,可以使用匿名内部类的方式调用;
匿名内部类的格式:
new 类名(){}:表示创建这个类的一个子类的对象;
new 接口名(){}:表示创建这个接口的一个实现类的对象;
2. java访问权限
java的访问权限有四种:
私有的,使用private关键字声明,表示被修饰的成员只能再定义他的那个类里面使用;
默认的,不使用任何关键字修饰,表示被修饰的成员只能在同一个类中或同一个包中的不同类之间使用;
受保护的,使用关键字protected修饰,表示使用范围比默认的多一点,就是可以在不同包中有继承关系的子类中使用;
公开的,使用关键字publiuc修饰,可以在任何地方使用;
30.png 31.png
结论:默认的访问权限,就是包访问权限,只能在同一个包中访问;
受保护的访问权限,不仅仅可以在同一个包中访问,还能在子类中使用;
java访问权限表:(√:可以访问; :不可访问)
32.jpg
java中的访问权限修饰符:
private:私有的被修饰的成员只能在本类中使用;
默认的(就是什么都不写):默认的,被修饰的成员只能在本类和相同一个包中不同的类中使用;
protected:受保护的,主要是给子类使用;使用范围和默认的相比,多一种场景可以使用,就是在不同包中不同的类,只要有继承关系,就可以使用;
public:公共的,公开的;被修饰的成员在所有地方都能使用;
3. API文档查询
3.1.API的概
API(Application programming interface):应用程序接口,指别人写好的供大家使用的程序;
API文档:帮助我们理解和使用API的文档就叫做API帮助文档;
打个比方,API就相当于别人制造好的手机,我们要打电话,直接调用手机打电话的功能就ok了;
而API文档,就是手机使用说明书,告诉我们如何使用手机;
3.2.API文档查询方法
3.2.1. 按包查询如果事先知道要查找的类在那个包中,就可以使用按包查询;
String类:它在java.lang包中;
33.png
3.2.2. 按索引查询
如果已经知道要找的类的类名,可以使用按索引查找;
找String类:
34.png 35.png 36.png
如果已经知道要找的类名,推荐使用这种方式,更快更准确;
如果根本就不知道要查找的功能在哪个类中,可以先上网搜索;
例如,要计算一个数字的平方根,经过网上搜索,发现应该使用Math类;知道了类名,就可以使用按照索引查找的方式,找到这个类,然后找到这个类的使用方式;
3.3.查看API文档描述
3.3.1. 类所在的包
37.png3.3.2. 类的继承关系
38.png3.3.3. 当前类实现的接口
39.png3.3.4. 类或者接口的定义格式
40.png(红框里的final,表示这个类是最终的类,不能被继承)
3.3.5. 类或者接口的功能描述
描述这个类或者接口该如何使用:
41.png 42.png
3.3.6. 类的构造方法
查看类的构造方法:
(有的类的构造函数都是私有的,就没有构造方法摘要,表示这个类不能通过构造函数创建对象;)
43.png
查看具体方法的详细说明(在方法摘要里直接点击方法名就可以跳转到方法详细说明):
44.png
3.3.7. 类的方法摘要
查看一个类提供的方法;
45.png
查看具体方法的详细说明(在方法摘要里直接点击方法名就可以跳转到方法详细说明):
46.png
3.3.8. 字段摘要
字段,就是类的成员变量;
前面的static,表示这是一个静态成员变量;
47.png
4. Object类简介
4.1.Object类简介
Object是java中所有类的最高父类,表示java中所有对象最共性的行为;
在使用多态时,可以使用Object类型指向java中的任何一个对象;
48.png 49.png
4.2.toString方法
49.png
50.png 51.png
父类方法不满足子类需求,需要在子类中重写该方法;
52.png
一般在开发中都要重写toString方法;
4.3.equals方法
53.png 54.png所以要比较两个对象的相等性,不能使用==比较,应该使用equals方法比较;!
55.png
所以需要在子类中重写equals方法;
56.png
在开发中一般都需要根据具体需求重写equals方法
重写equals方法的步骤(1和2步的顺序不是固定的):
1、判断参数和调用函数的对象是否是同一个对象,如果是,直接返回true;否则,进行下一步;
2、判断参数是否为null,如果是,直接返回false,否则,进行下一步;
3、判断参数和调用函数的类型是否一直,如果不一致,直接返回false,否则进行下一步;
4、对参数强制向下转型为调用方法的对象所在的类型,然后根据需求比较两个对象的具体细节;
4.4.Object小结
1、作用:Object类是java中所有类的最高父类,描述的是所有类最共性的功能;可以使用Object类型的引用指向java中所有对象(包括数组);
2、toString方法:
作用:toString方法会返回一个对象的字符串的表示形式;在使用输出指令输出一个对象时,实际输出的就是这个对象调用toString方法返回的结果;一般开发中都要根据具体需求重写这个方法;
格式:public String toString()
3、equals方法
作用:用来比较两个对象的相等性的;使用 == 运算符比较两个变量,实际比较的是两个变量里面保存的内容;
所以对基本类型的变量来说, == 比较的是实际的常量值;对引用类型的变量来说,比较的是里面保存的引用地址;所以要比较两个数据的相等性,基本类型的数据可以直接使用 == 比较,引用类型的数据则需要使用equals方法;因为Object类中的实现是直接使用 == 比较两个对象的引用地址,所以一般开发中需要根据具体的需求重写equals方法;
格式:public boolean equals(Object obj)
重写equals方法的一般步骤(1和2步的顺序不是固定的):
1、判断传入的参数obj是否为null(obj == null),如果是,直接返回false;
2、判断传入的参数和调用方法的对象是否是同一个对象(this == obj),如果是,直接返回true;
3、判断传入的参数指向的对象所属的类和调用方法的对象的类是否相同(this.getClass() == obj.getClass()),如果不同,直接返回false;
4、将传入的参数obj强制向下转型为调用方法的对象所在的类型,然后比较两个对象中各个应该比较的成员变量的值是否相等(具体比较哪些成员变量的值,根据需求而定)
网友评论