1. 内部类的概念
将类写在其它类的外部(可以在其它类的成员位置和局部位置),这时写在其它类内部的类就称为内部类,其它类称为外部类。
在描述事物时,若一个事物内部还包含其它可能包含的事物,比喻在描述汽车时,汽车中还包含发动机,这时发动机就可以用内部类来描述
1. class 汽车 { //外部类 2. class 发动机 { //内部类 3. } 4. }
2. 内部类的特点
- 内部类提供了更好的封装,只有外部类可以 访问内部类
- 内部类可以独立继承一个接口,不受外部类是否继承接口影响
- 内部类可以直接访问外部类的成员,包括私有private
- 外部类要访问内部类的成员,必须创建对象
- 在外部类中,即使内部类中用private修饰成员,也可以在外部类中用 内部类 对象.成员的方式访问
- private修饰内部类,则外部类以外不能访问,只能在外部类访问
3. 内部类的分类
内部类一般来说包括四种:成员内部类,局部内部类,匿名内部类和静态内部类。
定义内部类,就是正常定义类的过程,同样包括各种修饰符、继承与实现关系等。
3.1 成员内部类
3.1.1 成员内部类的一般形式
成员内部类是最普通的内部类,它的定义为位于另一个类的内部,一般形式如下:
1. class Circle { 2. double radius = 0; 3. 4. public Circle(double radius) { 5. this.radius = radius; 6. } 7. 8. class Draw { //内部类 9. public void drawSahpe() { 10. System.out.println("drawshape"); 11. } 12. } 13. }
这样看起来,类Draw像是类Circle的一个成员,Circle称为外部类。成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静态成员)。
3.1.2 内部类与外部类同名变量的处理
当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即默认情况下访问的是成员内部类的成员。如果要访问外部类的同名成员,需要以下面的形式进行访问:
1. 外部类.this.成员变量 2. 外部类.this.成员方法
3.1.3 外部类访问内部类成员的方法
虽然成员内部类可以无条件地访问外部类的成员,而外部类想访问成员内部类的成员却不是这么随心所欲了。在外部类中如果要访问成员内部类的成员,必须先创建一个成员内部类的对象,再通过指向这个对象的引用来访问:
1. class Circle { 2. private double radius = 0; 3. 4. public Circle(double radius) { 5. this.radius = radius; 6. getDrawInstance().drawSahpe(); //必须先创建成员内部类的对象,再进行访问 7. } 8. 9. private Draw getDrawInstance() { 10. return new Draw(); 11. } 12. 13. class Draw { //内部类 14. public void drawSahpe() { 15. System.out.println(radius); //外部类的private成员 16. } 17. } 18. }
3.1.4 创建内部类成员的一般形式
成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象。创建成员内部类对象的一般方式如下:
1. public class Test { 2. public static void main(String[] args) { 3. //第一种方式: 4. Outter outter = new Outter(); 5. Outter.Inner inner = outter.new Inner(); //必须通过Outter对象来创建 6. 7. //第二种方式: 8. Outter.Inner inner1 = outter.getInnerInstance(); 9. } 10. } 11. 12. class Outter { 13. private Inner inner = null; 14. public Outter() { 15. 16. } 17. 18. public Inner getInnerInstance() { 19. if(inner == null) 20. inner = new Inner(); 21. return inner; 22. } 23. 24. class Inner { 25. public Inner() { 26. 27. } 28. } 29. }
内部类可以拥有private访问权限、protected访问权限、public访问权限及包访问权限。比如上面的例子,如果成员内部类Inner用private修饰,则只能在外部类的内部访问,如果用public修饰,则任何地方都能访问;如果用protected修饰,则只能在同一个包下或者继承外部类的情况下访问;如果是默认访问权限,则只能在同一个包下访问。这一点和外部类有一点不一样,外部类只能被public和包访问两种权限修饰。我个人是这么理解的,由于成员内部类看起来像是外部类的一个成员,所以可以像类的成员一样拥有多种权限修饰。
3.2 局部内部类
3.2.1 局部内部类的概念
局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。
1. class People{ 2. public People() { 3. 4. } 5. } 6. 7. class Man{ 8. public Man(){ 9. 10. } 11. 12. public People getWoman(){ 13. class Woman extends People{ //局部内部类 14. int age =0; 15. } 16. return new Woman(); 17. } 18. }
注意,局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及static修饰符的。
3.2.2 局部内部类访问局部变量要用final修饰
- 局部内部类访问局部变量必须用
final
修饰 - jdk8或者更高版本,从语法上讲,不要求被局部内部类所访问的局部变量,一定要加
final
- 但是,如果在代码中,没有
final
,只要局部内部类访问局部变量,编译器会自动给局部变量加final
为什么一定要加final
因为局部变量会随着方法的调用而调用,随着调用完毕而消失。
而局部对象并没有立即从堆内存中消失,还要使用那个变量。所以,为了让数据还能继续被使用,就用 fianl
修饰,这样,在堆内存里面存储的其实是一个 常量值。
3.3 匿名内部类
3.3.1 概述
1. 定义:匿名内部类就是一个没有名字的局部内部类
2. 作用:匿名内部类是创建某个类型子类对象的快捷方式
3.3.2 特点
- 匿名内部类没有访问修饰符
- 匿名内部类必须继承一个类或者实现一个接口
- 匿名内部类中不能存在任何静态成员或方法
- 匿名内部类没有构造方法,因为它没有类名
- 匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效
- 匿名内部类不能是抽象的,所以它必须要实现继承的类或者实现接口的所有抽象方法
3.3.3 使用示例
一般使用匿名内部类的场景是,要继承或实现的接口只有一个抽象方法,比如添加一个监听器
1. public class Button { 2. public void click(){ 3. //匿名内部类,实现的是ActionListener接口 4. new ActionListener(){ 5. public void onAction(){ 6. System.out.println("click action..."); 7. } 8. }.onAction(); 9. } 10. //匿名内部类必须继承或实现一个已有的接口 11. public interface ActionListener{ 12. public void onAction(); 13. } 14. 15. public static void main(String[] args) { 16. Button button=new Button(); 17. button.click(); 18. } 19. }
3.4 静态内部类
3.4.1 概述
1. 定义:使用static修饰的成员内部类称之为静态内部类
2. 静态内部类的创建时不需要依赖位外部类,可以直接创建
3.4.2 访问特征
- 对于 静态内部类 而言,它不能访问外部类中 非静态的 成员变量和成员方法
- 在 外部类中 访问,静态内部类,和访问普通成员内部类没有任何区别
- 在 外部类的外部 访问静态内部类,由于静态内部类,不依赖于外部类对象 :
new 外部类类名.内部类类名()
3.4.3 使用示例
1. public class OuterClass { 2. private static String outerName; 3. public int age; 4. 5. static class InnerClass1{ 6. /* 在静态内部类中可以存在静态成员 */ 7. public static String _innerName = "static variable"; 8. public void display(){ 9. /* 10. * 静态内部类只能访问外部类的静态成员变量和方法 11. * 不能访问外部类的非静态成员变量和方法 12. */ 13. System.out.println("OutClass name :" + outerName); 14. } 15. } 16. class InnerClass2{ 17. /* 非静态内部类中不能存在静态成员 */ 18. public String _innerName = "no static variable"; 19. /* 非静态内部类中可以调用外部类的任何成员,不管是静态的还是非静态的 */ 20. public void display(){ 21. System.out.println("OuterClass name:" + outerName); 22. System.out.println("OuterClass age:" + age); 23. } 24. } 25. public void display(){ 26. /* 外部类能直接访问静态内部类静态元素 */ 27. System.out.println(InnerClass1._innerName); 28. /* 静态内部类可以直接创建实例不需要依赖于外部类 */ 29. new InnerClass1().display(); 30. /* 非静态内部的创建需要依赖于外部类 */ 31. OuterClass.InnerClass2 inner2 = new OuterClass().new InnerClass2(); 32. /* 非静态内部类的成员需要使用非静态内部类的实例访问 */ 33. System.out.println(inner2._innerName); 34. inner2.display(); 35. } 36. 37. public static void main(String[] args) { 38. OuterClass outer = new OuterClass(); 39. outer.display(); 40. } 41. }
参考链接: