这篇博客是对《java编程的逻辑》这本书中的java内部类做的一些笔记。
文章很长,请耐心看。
内部类分类:
-
静态内部类
-
成员内部类
-
方法内部类
-
匿名内部类
- 静态内部类
- 实例代码:
package innerclass; public class Outer1 { private static int shared = 100; public static class staticInnerclass{ public void innerMathod() { System.out.println("inner "+shared); } } public void test() { staticInnerclass siInnerclass = new staticInnerclass(); siInnerclass.innerMathod(); } public static void main(String args[]) { Outer1.staticInnerclass sic = new Outer1.staticInnerclass(); sic.innerMathod(); } }
外部类为Outer1,静态内部类为StaticInnerclass,带有static 修饰符。
可访问外部类的静态变量和方法,但不可以访问实例变量和方法。
public静态内部类可以被外部使用,我们使用的时候可以使用“外部类.内部类”的方式去使用。 - 内部实现:
静态内部类是如何实现的呢?实际上静态内部类生成了两个类,一个是Outer1,一个是Outer1.staticInnerclass。package innerclass; class Outer$staticInner { public void innerMathod() { System.out.println("inner "+Outer12.access$0()); } } public class Outer12 { private static int shared = 100; public static class staticInnerclass{ } public void test() { Outer$staticInner siInnerclass = new Outer$staticInner(); siInnerclass.innerMathod(); } static int access$0() { return shared; } public static void main(String args[]) { Outer1.staticInnerclass sic = new Outer1.staticInnerclass(); sic.innerMathod(); } }
原理解析:内部类访问了外部类的一个私有静态变量shared,我们知道私有变量不能被类外部访问,所以解决办法是
自动生成一个非私有访问方法access$0,它返回这个私有静态变量shared。 -
使用场景以及JavaAPI中的例子:
使用场景:与外部类关系密切,且不依赖于外部类实例,则可以使用静态内部类。
例子:
1.Integer类内部的私有静态内部类IntegerCache
2.Character类内部有一个public静态内部类UnicodeBlock
- 成员内部类
- 实例代码
package innerclass; public class Outer2 { private int a = 100; public class Inner{ public void innerMethod() { System.out.println("outer:"+a); Outer2.this.action(); } } public void action() { System.out.println("action"); } public void test() { Inner inner = new Inner(); inner.innerMethod(); } public static void main(String[] args) { // TODO Auto-generated method stub Outer2 outer2 = new Outer2(); Outer2.Inner inner = outer2.new Inner(); inner.innerMethod(); } }
外部类为Outer2,静态内部类为Inner,相对于静态内部类不带有带有static 修饰符。
可访问外部类的静态变量和方法,还可以访问实例变量和方法。
成员内部类可以通过“外部类.this.MethodName”的方式引用外部类的实例变量和方法。
成员内部类对象总是与一个外部类对象相连的。所以我们在使用内部类的时候先要创建外部类。 - 内部实现
package innerclass; class Outer22 { private int a = 100; public void action() { System.out.println("action"); } public void test() { Outer$Inner inner = new Outer$Inner(this); inner.innerMethod(); } static public int access$0(Outer22 outer2) { return outer2.a; } static public void access$1(Outer22 outer) { outer.action(); } } class Outer$Inner{ final Outer22 outer2; public Outer$Inner(Outer22 outer2) { this.outer2 = outer2; } public void innerMethod() { System.out.println("outer:"+Outer22.access$0(outer2)); outer2.action(); } } public class MainClass22{ public static void main(String[] args) { // TODO Auto-generated method stub Outer22 outer2 = new Outer22(); Outer$Inner inner = new Outer$Inner(outer2); inner.innerMethod(); } }
为什么使用final关键字呢?
内部类是与外部实例相连的(对于内部类来说,外部类应为内部类的构造因子),不应该单独使用,而静态变量和方法作为类型的属性和方法,一般作为独立使用(也就是说我们如果需要,则可以挪到外部类中)。 - 使用场景以及JavaAPI中的例子:
使用场景:内部类与外部类关系密切,需要访问外部类的实例变量或者方法,则可以考虑定义为成员内部类。
例子:
LinkedList类中listIterator和descendingIterator的返回值都是Iterator
- 方法内部类
- 实例代码
package innerclass; class Outer3class { private int a = 100; public void show() { System.out.println("the show func"); } public void test( int param) { final String string = "hello"; class Inner{ public void InnerMethod() { show(); System.out.println("outer a "+a); System.out.println("param "+param); System.out.println("local var "+string); } } Inner inner = new Inner(); inner.InnerMethod(); } } public class Outer3 { public static void main(String[] args) { // TODO Auto-generated method stub new Outer3class().test(1); } }
方法内部类只能在定义的方法内使用。方法内部类还可以直接访问方法的参数
实例方法:除了静态变量和方法,内部类还可以直接访问外部类的实例变量和方法。如上面的test()。
静态方法:只能访问外部类的静态变量和方法。(具体可以看https://blog.csdn.net/zx48822821/article/details/52575631) - 内部实现
package innerclass; class Outer31class { static private int a = 100; static public int access$0(Outer31class outer3class) { return Outer31class.a; } public void test(final int param) { final String string = "hello"; OuterInner inner = new OuterInner(this,param); inner.InnerMethod(string); } } class OuterInner{ private Outer31class outer; private int param; public OuterInner(Outer31class outer,int param) { this.outer = outer; this.param = param; } public void InnerMethod(String string) { System.out.println("outer a "+Outer31class.access$0(this.outer)); System.out.println("param "+param); System.out.println("local var "+string); } } public class MainClass32 { public static void main(String[] args) { // TODO Auto-generated method stub new Outer31class().test(3); } }
以上的代码与成员内部类类似,内部类OuterInner也有一个实例变量outer指向外部对象,在构造方法中被初始化(所以先new外部类),方法内部类可以访问方法中的参数和局部变量(通过构造方法中传递参数或者引用方法传递来实现的)。
方法内部类的操作的并不是外部的变量,而是它自己的实例变量。也就是说我们是用过构造函数传参和在实例化过的外部类的方法传参情况下去实现了方法内部类。
以上所说的变量的值和外部一样,对于这些变量赋值,并不会改变外部的值,为了避免混淆,所以直接干脆规定声明为final。但是有时候的确需要修改外部类的变量,那么我们可以改为只含该变量的数组,通过修改arrayName[0]的值。代码如下:public void test( int param) { final String string[] = {"hello"}; class Inner{ public void InnerMethod() { show(); string[0] = "i am cat"; System.out.println("outer a "+a); System.out.println("param "+param); System.out.println("local var "+string[0]); } } Inner inner = new Inner(); inner.InnerMethod(); }
- 使用场景以及JavaAPI中的例子:
使用场景:类只在某个方法内被使用,使用方法内部类可以实现更好的封装。
- 匿名内部类
- 实例代码
匿名内部类没有单独的类定义,他在创建对象的同时定义类,语法如下:new 父类(参数列表) { //匿名内部类内部实现部分 } new 父接口(参数列表) { //匿名内部类内部实现部分 }
package innerclass; import java.awt.Point; class Point1 extends Point { private int x; private int y; public Point1(int x,int y) { super(x,y); } public double distance() { return 0; } } class Outer4class{ public void test(final int x,final int y) { Point1 point = new Point1(1,1) { @Override public double distance() { return this.distance(x,y); } }; System.out.println(point.distance()); } } public class Outer4 { public static void main(String[] args) { // TODO Auto-generated method stub new Outer4class().test(3, 4); } }
因为书上给的代码错误,所以自己写了一个继承关系。
父类为Point,创建对象的时候,给父类构造方法传递了参数两个0,重写了distance()方法,在方法内部访问了外部方法final参数x,y。 - 内部实现
package innerclass; import java.awt.Point; class Outer$1 extends Point1{ private int x2; private int y2; Outer42class outer42class; public Outer$1(Outer42class outer42class,int x1,int y1,int x2,int y2) { super(x1, y1); this.outer42class = outer42class; this.x2 = x2; this.y2 = y2; } @Override public double distance() { return this.distance(this.x2,this.y2); } } class Outer42class{ public void test(final int x,final int y) { Point1 point = new Outer$1(this,2,3,x,y); System.out.println(point.distance()); } } public class MainClass42 { public static void main(String[] args) { // TODO Auto-generated method stub new Outer42class().test(2, 2); } }
匿名内部类是怎么实现的呢?每个匿名内部类也都被生成一个独立的类,只是类的名字以外部类加数字编号,没有又意义的名字。
- 使用场景以及JavaAPI中的例子:
使用场景:如果对象只会创建一次,且不需要构造方法来接收参数,则可以使用匿名内部类实现更加简洁。
例子:回调事件。