一、静态内部类:
1、声明的语法
声明在成员变量的位置,使用static修饰
public static class{}
2、内部类可以直接访问外部类的哪些内容
只能直接访问外部类的静态成员变量;
若要访问外部类的实例成员,需要通过外部类的实例进行访问;
3、外部类可以直接访问内部类的哪些内容
可以通过类名访问静态成员
通过内部类的实例访问内部类的实例成员
4、内部类对象的构建
外部类名.内部类名 inner = new 外部类名.内部类名();
【重点】 为什么静态内部类不能直接访问外部类的实例成员(也不能使用 外部类.this)?
this是与实例对象有关的,静态内部类是与外部类有关的,也就是说在外部类第一次类加载的时候静态内部类就已经初始化完成了,而这时候外部类的实例还不一定存在,所以不能直接使用 外部类名.this 的方式访问外部类的实例成员,需要通过new关键字构建外部类实例的方式来访问
二、实例内部类:
1、声明的语法
声明在成员变量的位置
public class{}
2、内部类可以直接访问外部类的哪些内容
可以直接访问外部类的实例成员、静态成员;
3、外部类可以直接访问内部类的哪些内容
实例内部类中只能有实例成员变量;
外部类只能通过内部类的实例访问内部类的实例成员;
4、内部类对象的构建
外部类 out = new 外部类();
外部类.内部类 inner = out.new 内部类();
外部类.内部类 inner = new 外部类().new Inner();
【重点】1、自己内部类中定义的方法和属性,与外部类中定义的方法和属性重名的情况下如何访问?
(1)在内部类中直接访问重名属性,会默认调用内部类中的属性和方法
(2)若内部类中没有这个属性和方法,会调用外部类中的属性和方法
(3)若要调用外部类中的属性和方法,使用 外部类名.this.属性/方法()
2、为什么实例内部类的成员能够直接访问外部类的实例成员和静态成员?
(1)静态成员是与类相关,也就是说类在第一次加载的时候就已经将类中的静态成员初始化完成了,所以内部类能够直接访问静态成员。
(2)实例内部类和类的实例变量处于同一个位置,也就是说,实例内部类是与类的实例相关的,当使用内部类的时候外部类的实例是已经存在了的,所以可以使用外部类中的实例属性。
三、局部内部类:
1、声明的语法
声明在方法中
class inner{}
2、内部类可以直接访问外部类的哪些内容
能够访问内部类所在方法的外部类的所有属性(静态属性、实例属性),此外,局部内部类还可以访问所在方法中的final类型的参数和变量。
3、外部类可以直接访问内部类的哪些内容
内部类中不能包含静态属性和方法;
外部类不能访问内部类的任何成员;
但是内部类所在的方法中能够通过内部类的实例来访问内部类的成员变量;
4、内部类对象的构建
在内部类所在的方法中通过以下方式构建对象
内部类名 inner = new 内部类名();
【重点】1、类中的局部变量、成员变量、方法的存储位置分别在哪?
- 方法中的局部变量:(1)基本数据类型,会存放在栈中;当调用这个方法时,会将这个方法压入栈中,并且会创建一个代码块专门存放方法的变量,方法完成之后,方法中的所有变量都出栈(2)通过new出来的对象,对象存放在堆中,对象的引用存放在栈中。
- 类中的成员变量:同一个类的不同对象的成员变量是各不相同的,这些成员变量都有自己的存储空间(存储在堆中的对象中),基本数据类型和引用数据类型的成员变量都在这个对象的空间中,作为一个整体存储在堆中。
- 类中的方法:类中的方法是所有对象所共享的,只有一套,对象使用方法的时候才被压入栈,方法不使用时则不占用内存。
2、为什么局部内部类不能直接访问内部类所在方法中的局部变量,但是能够使用用final修饰的变量?
内部类的生命周期和方法中的局部变量是不一样的,内部类是也是一个类,是存储在堆中,也只有当对该类的引用消失时,内部类才会消亡。
而方法的局部变量是存储在栈中的,当调用结束时就会退栈,即在内存中这个属性就消失了。
这个时候,局部对象并没有立马从堆内存中消失,局部对象还要使用这个局部变量。
为了让数据还能继续被使用,就用fianl修饰局部变量,这样,局部变量就会存在堆中
四、匿名内部类:(接口,抽象类;具体类也可以使用)
1.声明语法
声明在成员变量的位置(相当于成员内部类),或者是局部变量的位置(相当于局部内部类)
父类类型 name = new 父类类型(){
方法重写或者方法的实现
};
2、内部类可以直接访问外部类的哪些内容
当匿名内部类声明在成员变量的位置,该内部类就相当于成员内部类,可以直接访问外部类的实例成员和静态成员;
当匿名内部类声明在局部成员的位置,该内部类就相当于局部内部类,可以直接访问外部类的实例成员和静态成员,还可以访问局部内部类所在方法用final修饰的局部变量;
(匿名内部类的主要功能是实现父类/接口中的方法,是一个没有名字的内部类,写在方法中居多)
3、外部类可以直接访问内部类的哪些内容
成员内部类:。。。
局部内部类:外部类不能访问内部类的任何成员;
4、内部类对象的构建
匿名内部类对象的构建和匿名内部类的声明是放到一起的
【重点】1、匿名内部类是用到最多的一个内部类,一般在当一个接口或者类中的方法与你想构建的方法名字相同的时候使用,这时还可以不用为这个类取名字;
子类没有名字,所以不能调用子类的构造器来构建对象,
只能在声明这个匿名内部类的同时使用new关键字借助父类的构造器将这个匿名子类给构建出来
//son实际上是father这个类的子类对象
Father son = new Father(){
//方法的实现或方法的重写
}
2、最典型匿名内部类使用的地方
TreeSet集合添加对象时,TreeSet集合是不能添加相等的对象,不能添加基本类型的数据,只能添加同一种类型的对象,否则会出现类型转换异常,并且能够为添加进来的对象进行排序:(有关集合的详细内容我会写在我下一篇博客中)
自然排序:添加进TreeSet集合的对象所属的类必须实现了Comparable接口,对compareTo方法进行实现;
compareTo(Object o1):return 1:交换位置 0:表示o1这个对象和调用conpareTo方法的是同一个对象 -1:不交换位置
(Double、String、Integer等包装器类型已经对Comparable接口进行了实现)
客户化排序:在创建TreeSet对象时指定一个排序规则;使用Comparator接口,实现了compare方法(与compreTo方法类似)
优点:这样就算没有实现Comparable接口的类的对象也能加入到TreeSet集合中
TreeSet set = new TreeSet(new Comparator(){
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof Student && o2 instanceof Student) {
Student stu1 = (Student)o1;
Student stu2 = (Student)o2;
return stu1.getName().compareTo(stu2.getName());
}
return 0;
}
});
java内部类有什么好处?为什么需要内部类?
首先举一个简单的例子,如果你想实现一个接口,但是这个接口中的一个方法和你构想的这个类中的一个方法的名称,参数相同,你应该怎么办?这时候,你可以创建一个内部类实现这个接口。由于内部类对于外部类的所有内容都是可以访问的,所以这样做可以完成你直接实现这个接口的功能。
不过你可能要质疑,更改一下方法不就可以了吗?
的确,以此作为设计内部类的理由,没有说服力。
真正的原因是这样的,java中的内部类和接口加在一起,可以解决常被C++程序员抱怨java中存在的一个问题 没有多继承。实际上,c++的多继承设计起来很复杂,而java通过内部类+接口,可以很好的实现多继承的效果。