1.非静态成员内部类
| public class OuterClass { //成员变量 private String name; private int num = 10; //构造方法 public OuterClass() { } public OuterClass(String name, int num) { this.name = name; this.num = num; } //成员方法 public void methodOut(){ System.out.println("methodOut"); } public void methodOut2(){ //外部类不可以直接访问内部的的成员变量和成员方法 //System.out.println(type); //methodInner(); InnerClass ic = new InnerClass(); System.out.println(ic.num);//20 ic.methodInner(); } //内部类 class InnerClass{ //成员变量 private String type; private int num = 20; //构造方法 public InnerClass() { } public InnerClass(String type, int num) { this.type = type; this.num = num; } //成员方法 public void methodInner(){ //内部类可以直接访问外部类的成员变量 System.out.println(name); int num = 30; System.out.println(num); //30 System.out.println(this.num); //20 //内部类如何访问外部类的同名成员变量 System.out.println(OuterClass.this.num);//10 methodOut(); } } } public class Test { public static void main(String[] args) { OuterClass oc = new OuterClass(); oc.methodOut(); oc.methodOut2(); //要创建 非静态成员内部类对象,必须先创建外部类的对象 //OuterClass.InnerClass ic = new OuterClass().new InnerClass(); OuterClass oc2 = new OuterClass(); OuterClass.InnerClass ic =oc2.new InnerClass(); } } |
总结:基本特征
- 内部类可以直接访问外部类的成员
- 外部类不能直接访问内部类的成员,需要先创建对象再通过对象名访问
- 内部类如何访问外部类的同名成员变量:OuterClass.this.num
- 必须先创建外部类的对象,才能创建内部类的对象。非静态成员内部类是属于某个外部类对象的
- 非静态内部类不能有静态方法、静态属性和静态初始化块。
- 外部类的静态方法、静态代码块不能访问非静态内部类,包括不能使用非静态内部类定义变量、创建实例。
| 注意 内部类只是一个编译时概念,一旦我们编译成功,就会成为完全不同的两个类。对于一个名为Outer的外部类和其内部定义的名为Inner的内部类。编译完成后会出现Outer.class和Outer$Inner.class两个类的字节码文件。所以内部类是相对独立的一种存在,其成员变量/方法名可以和外部类的相同。 |
2. 静态成员内部类
| public class OuterClass { //成员变量 private static String name; private static int num = 10; //构造方法 public OuterClass() { } public OuterClass(String name, int num) { this.name = name; this.num = num; } //成员方法 public static void methodOut(){ System.out.println("methodOut"); InnerClass ic = new InnerClass(); InnerClass.methodInner2(); } public void methodOut2(){ //外部类不可以直接访问内部的的成员变量和成员方法 InnerClass ic = new InnerClass(); System.out.println(ic.num);//20 ic.methodInner(); InnerClass.methodInner2(); } //内部类 static class InnerClass{ //成员变量 private String type; private int num = 20; //构造方法 public InnerClass() { } public InnerClass(String type, int num) { this.type = type; this.num = num; } //成员方法 public void methodInner(){ //静态内部类只能够访问外部类的静态成员 System.out.println(name); int num = 30; System.out.println(num); //30 System.out.println(this.num); //20 //静态内部类如何访问外部类的同名的成员变量 System.out.println(OuterClass.num);//10 methodOut(); } public static void methodInner2(){
} } } public class Test { public static void main(String[] args) { //要创建静态成员内部类对象,不需要先创建外部类的对象 OuterClass.InnerClass ic =new OuterClass.InnerClass(); //需要import com.bjsxt.innerclass2.OuterClass.InnerClass; InnerClass ic2 = new InnerClass(); } } |
总结:
- 静态内部类只能够访问外部类的静态成员
- 静态内部类如何访问外部类的同名的成员变量:OuterClass.num
- 静态内部类属于整个外部类的。创建静态内部类的对象,不需先创建外部类的对象
- 外部类可以通过类名直接访问内部类的静态成员,访问非静态成员依旧需要先创建内部类对象。
3.局部内部类
它是定义在方法内部的,作用域只限于本方法,称为局部内部类。局部内部类的的使用主要是用来解决比较复杂的问题,想创建一个类来辅助我们的解决方案,到那时又不希望这个类是公共可用的,所以就产生了局部内部类。局部内部类和成员内部类一样被编译,只是它的作用域发生了改变,它只能在该方法中被使用,出了该方法就会失效。 局部内部类在实际开发中应用很少。
| public class OuterClass { int num1 = 10; public void method(){ int num2 = 20; class InnerClass{ public void method2(){ num1 = 100; System.out.println(num2); //num2 = 200; } } InnerClass ic = new InnerClass(); ic.method2(); } } |
注意:局部内部类访问所在方法的局部变量,要求局部变量必须使用final修饰。JDK1.8中final可以省略,但是编译后仍旧会加final。
4.匿名内部类(开发中使用较多)
匿名内部类就是内部类的简化写法,是一种特殊的局部内部类。
前提:存在一个类或者接口,这里的类可以是具体类也可以是抽象类。
本质是什么呢?是一个继承了该类或者实现了该接口的子类匿名对象。
适合那种只需要创建一次对象的类。比如:Java GUI编程、Android编程键盘监听操作等等。比如Java开发中的线程任务Runnble、外部比较器Comparator等。
语法:
| new 父类构造器(实参类表) 实现接口 () { //匿名内部类类体! } |
问题:一个类实现Comparable接口,只能指定一种比较大小的规则,如果需要有更多的比较规则,怎么办?
Comparable:内部比较器 public class Student implements Comparable{} 内部比较器只能有一个,一般采用最经常使用的比较规则
Comparator 外部比较器 可指定多个 不需要Student实现该接口,而是定义专门的类。
(1)定义Comparator接口
| public interface Comparator{ public int compare(Object obj1, Object obj2); } |
(2)实现Comparator接口的类
| public class BookNameComparator implements Comparator1 { @Override public int compare(Object obj1, Object obj2) { Book book1 = (Book)obj1; Book book2 = (Book)obj2; return book1.getBookName().compareTo(book2.getBookName()); } } public class BookPriceNameComparator implements Comparator1 { @Override public int compare(Object obj1, Object obj2) { Book book1 = (Book)obj1; Book book2 = (Book)obj2; if(book1.getPrice() > book2.getPrice()){ return -1; }else if(book1.getPrice()< book2.getPrice()){ return 1; }else{ return book1.getBookName().compareTo(book2.getBookName()); } } } |
如果某个外部比较器只使用一次或者很少的次数,就可以不提供专门的类,而是使用匿名内部类。
(3)使用匿名内部类实现外部比较器
| public class Test { public static void main(String[] args) { Comparable comp; Comparator comp2; Book book1 = new Book("倚天屠龙记1","金庸1","清华大学出版社",35); Book book2 = new Book("倚天屠龙记5","金庸5","清华大学出版社",35); int result = book1.compareTo(book2); System.out.println(result); Comparator1 cmp1 = new BookNameComparator(); result = cmp1.compare(book1,book2); System.out.println(result); Comparator1 cmp2 = new BookPriceNameComparator(); result = cmp2.compare(book1,book2); System.out.println(result);
Comparator1 cmp3 = new Comparator1() { //代码块,每次创建对象的时候执行,并且早于构造方法执行 { System.out.println("--匿名内部类通过代码块完成初始化操作---"); } @Override public int compare(Object obj1, Object obj2) { Book book1 = (Book)obj1; Book book2 = (Book)obj2; return book1.getAuthor().compareTo(book2.getAuthor()); } }; result = cmp3.compare(book1,book2); System.out.println(result); } } |
结论:
- 匿名内部类可以实现一个接口,也可以继承一个类(可以是抽象类)。
- 匿名内部类只能实现一个接口,而不是多个
- 必须实现所有的方法,匿名内部类不能是抽象类
- 匿名内部类不可能有构造方法,因为类是匿名的
- 匿名内部类没有访问修饰符
- 如果想实现构造方法的一些初始化功能,可以通过代码块实现
- 如果要访问所在方法的局部变量,该变量需要使用final修饰。(JDK1.8可省略final)
内部类的作用和使用场合
内部类的作用:
- 内部类提供了更小的封装。只能让外部类直接访问,不允许同一个包中的其他类直接访问。
- 内部类可以直接访问外部类的私有属性,内部类被当成其外部类的成员。 但外部类不能访问内部类的内部属性。
- 接口只是解决了多重继承的部分问题,而内部类使得多重继承的解决方案变得更加完整。
- 用匿名内部类实现回调功能。我们用通俗讲解就是说在Java中,通常就是编写一个接口,然后你来实现这个接口,然后把这个接口的一个对象作以参数的形式传到另一个程序方法中, 然后通过接口调用你的方法,匿名内部类就可以很好的展现了这一种回调功能
内部类的使用场合:
- 由于内部类提供了更好的封装特性,并且可以很方便的访问外部类的属性。所以,在只为外部类提供服务的情况下可以优先考虑使用内部类。
- 使用内部类间接实现多继承:每个内部类都能独立地继承一个类或者实现某些接口,所以无论外部类是否已经继承了某个类或者实现了某些接口,对于内部类没有任何影响。