内部类 内部类的基本使用 内部类概念:在一个类中定义一个类
比如,在一个类 A 的内部定义一个类 B,类 B 就被称为内部类。内部类定义格式如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Outer { public class Inner { } }
内部类的访问特点
内部类可以直接访问外部类的成员,包括私有
外部类要访问内部类的成员,必须创建对象
1、示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class Test1Inner { public static void main (String[] args) { Inner i = new Inner(); } } class Outer { private int a = 10 ; class Inner { int num = 10 ; public void show () { System.out.println("Inner..show" ); } } }
以上代码中,为什么这里编译器会报错呢:
因为类名没有写全 –> 为什么呢?举例如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class Test1Inner { public static void main (String[] args) { Inner i = new Inner(); } } class Outer { private int a = 10 ; class Inner { } } class Outer2 { class Inner { } }
此时创建 Inner 对象时,编译器都懵掉了,你到底是想要创建那个类中的 Inner 类 –> 需要我们正确书写格式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class Test1Inner { public static void main (String[] args) { Outer.Inner i = new Outer().new Inner () ; } } class Outer { private int a = 10 ; class Inner { } }
emm,创建内部类为什么格式那么复杂:-)。如下是演示内部类变量、内部类方法的访问方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class Test1Inner { public static void main (String[] args) { Outer.Inner i = new Outer().new Inner () ; System.out.println(i.num); i.show(); } } class Outer { private int a = 10 ; class Inner { int num = 10 ; public void show () { System.out.println("Inner...show" ); } } }
内部类是可以直接使用外部类中的成员变量的,代码如下:
可以看到,调用内部类方法时,可以访问到外部类中的 a 变量(包括私有的成员变量)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class Test1Inner { public static void main (String[] args) { Outer.Inner i = new Outer().new Inner () ; System.out.println(i.num); i.show(); } } class Outer { private int a = 10 ; class Inner { int num = 10 ; public void show () { System.out.println("Inner...show" ); System.out.println("外部类中的成员变量" + a); } } }
程序输出
1 2 3 10 Inner...show 外部类中的成员变量10
成员内部类 按照内部类在类中定义的位置不同,可以分为如下两种形式
在类的成员位置:成员内部类
在类的局部位置:局部内部类
外界创建成员内部类格式
1 2 Outer.Inner oi = new Outer().new Inner () ;
成员内部类,也属于(成员),既然是成员就可以被一些修饰符所修饰,比如 private 和 static
私有成员内部类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class Test2Innerclass { public static void main (String[] args) { Outer.Inner oi = new Outer().new Inner () ; } } class Outer { private class Inner { public void show () { System.out.println("inner..show" ); } } }
此处,创建成员内部类对象时编译错误 因为 private 是同一类中可见 –> 也就是说只在 Outer 类中是可见的。那么外界如何创建这个内部类对象呢:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class Test2Innerclass { public static void main (String[] args) { Outer outer = new Outer(); outer.method(); } } class Outer { private class Inner { public void show () { System.out.println("inner..show" ); } } public void method () { Inner i = new Inner(); i.show(); } }
小结 私有成员内部类访问:在自己所在的外部类中创建对象访问
静态成员内部类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Test3Innerclass { public static void main (String[] args) { Outer.Inner oi = new Outer.Inner(); oi.show(); } } class Outer { static class Inner { public void show () { System.out.println("inner..show" ); } } }
1 2 Outer.Inner oi = new Outer().new Inner () ; Outer.Inner oi = new Outer.Inner();
因为 Inner 不是 private 修饰的,所以外部可以访问
因为 Inner 是 static 修饰的,可以不用创建类的对象『new Outer ()』来访问,通过类『Outer』直接就可以访问了
对于 Staic 修饰的其实很好调用,比如静态内部类中还有一个静态方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Test3Innerclass { public static void main (String[] args) { Outer.Inner oi = new Outer.Inner(); Outer.Inner.method(); } } class Outer { static class Inner { public static void method () { System.out.println("inner..method" ); } } }
小结 静态成员内部类访问:
1 外部类名.内部类名 对象名 = new 外部类名.内部类名();
静态成员内部类中的静态方法:
局部内部类 局部内部类定义位置:局部内部类是在方法中定义的类,所以外界是无法直接访问的,需要在方法内部创建对象并使用
该类可以直接访问外部类的成员,也可以访问方法内地局部变量
1、代码展示:调用局部内部类中的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class Test4Innerclass { public static void main (String[] args) { Outer o = new Outer(); o.method(); } } class Outer { public void method () { class Inner { public void show () { System.out.println("show..." ); } } Inner inner = new Inner(); inner.show(); } }
因为局部内部类的访问范围仅仅是方法体中的范围,范围如下图所示
2、代码展示:局部内部类访问不同变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public class Test4Innerclass { public static void main (String[] args) { Outer o = new Outer(); o.method(); } } class Outer { int a = 10 ; public void method () { int b = 20 ; class Inner { public void show () { System.out.println("show..." ); System.out.println(a); System.out.println(b); } } Inner inner = new Inner(); inner.show(); } }
小结
局部内部类,外界是无法直接使用,需要在方法内部创建对象并使用
该类可以直接访问外部类的成员,也可以访问方法内的局部变量
局部内部类我们平时是很少编写的,因为局部内部类实在是太受限了;在看源码的过程中也很少会见到局部内部类,讲他的作用是为后面的匿名内部类打基础。因为匿名内部类属于一种特殊的局部内部类
匿名内部类 匿名内部类本质上说一个特殊的局部内部类(定义在方法内部) 前提:需要存在一个接口或类
格式如下:
1 2 3 4 5 6 7 8 9 10 new Inter(){ @Override public void method () {} }
1、代码演示:正常使用接口中的方法需要几步
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Test5Innerclass { public static void main (String[] args) { InterImpl ii = new InterImpl(); ii.show(); } } interface Inner { void show () ; } class InterImpl implements Inner { @Override public void show () { System.out.println("InterImpl 重写的show方法" ); } }
1)创建实现类,通过 implements 关键字去实现接口 2)重写方法 3)创建实现类对象 4)调用重写后的方法
可以看到,想要使用接口中的方法还是比较复杂的。而如果是通过匿名内部类,就可以化简为 1 步
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class Test5Innerclass { public static void main(String[] args) { - InterImpl ii = new InterImpl(); - ii.show(); //匿名内部类 + new Inner(){ + @Override + public void show() { + System.out.println("匿名内部类中的show方法"); + } + }.show(); } } interface Inner{ void show(); } -class InterImpl implements Inner{ - @Override - public void show() { - System.out.println("InterImpl 重写的show方法"); - } -}
我们重点分析这几行代码
1 2 3 new Inner(){ }.show();
可以认为
匿名内部类的理解:将继承(或实现)、方法重写、创建对象这三步放在了一步都当完成
1 2 new Inner(){}; --> 相当于创建了一个实现了接口的实现类对象new Inner(){}.show(); --> 实现类对象调用方法
2、代码演示:如果接口中有多个方法,匿名内部类最多只能调用其中的一个方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class Test5Innerclass { public static void main (String[] args) { new Inner(){ @Override public void show1 () { System.out.println("匿名内部类中的show1方法" ); } @Override public void show2 () { System.out.println("匿名内部类中的show2方法" ); } }.show1(); } } interface Inner { void show1 () ; void show2 () ; }
此时,我就想调用其中的多个方法呢,怎么办
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class Test5Innerclass { public static void main (String[] args) { Inner i = new Inner(){ @Override public void show1 () { System.out.println("匿名内部类中的show1方法" ); } @Override public void show2 () { System.out.println("匿名内部类中的show2方法" ); } }; i.show1(); i.show2(); } } interface Inner { void show1 () ; void show2 () ; }
案例小结:如果说一个接口当中有多个方法,如果使用匿名内部类的方式将其中的多个方法都进行调用,可以在匿名内部类前面通过一个父类或父接口的引用去接收一下,这样就能以多态的形式将匿名内部类接受过来。通过引用就可以调其中的方法了
匿名内部类在开发中的使用 当方法的形式参数是接口或者抽象类时,可以将匿名内部类作为实际参数进行传递
1、代码演示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class TestSwimming { public static void main (String[] args) { goSwimming(new Swimming(){ @Override public void swim () { System.out.println("GoGoGo!" ); } }); } public static void goSwimming (Swimming swimming) { swimming.swim(); } } interface Swimming { void swim () ; }
此处蓝色标记的就是创建了一个匿名内部类,将其作为 goSwimming () 方法的参数
既然匿名内部类作为方法参数,其格式比较固定,编译器也为我们提供了代码提示:只要输入 new 父接口名
,根据提示回车即可自动生成代码段
Lambda 表达式 Lambda 表达式可以认为是对匿名内部类的优化,在了解这一块知识前,请先熟悉匿名内部类
匿名内部类和 Lambda 表达式 1、代码演示:匿名内部类和 Lambda 表达式的使用方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class TestSwimming { public static void main (String[] args) { goSwimming(new Swimming() { @Override public void swim () { System.out.println("铁汁, 我们去游泳吧" ); } }); goSwimming(() -> System.out.println("铁汁, 我们去游泳吧" )); } public static void goSwimming (Swimming swimming) { swimming.swim(); } } interface Swimming { void swim () ; }
小结:Lambda 表达式可以使关注点更加明确,为什么这么说呢,请看一下分析:
1、对于匿名内部类的方式(面向对象思想,以什么形式去做) 1)方法要一个接口,我得给个接口的实现类对象 2)创建匿名内部类对象,重新方法 3)方法要干嘛呢,其实就是打印一句话
而其实,我们想要做的仅仅是打印一句话,也就是想要完成第 3 步,而第 1 步和第 2 步都是附加的操作,是 “不得不” 才这样写的
2、对于 Lambda 表达式(函数式编程思想,更多关注做什么)
Lambda 表达式的标准格式
组成 Lambda 表达式的三要素:形式参数、箭头、代码块
形式参数:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可
->:表示将小括号中的形式参数传到大括号的代码块中进行处理
代码块:是我们具体要做到事情,也就是方法体中的内容
Lambda 表达式的使用前提:
有一个接口(也就表明 Lambda 只能操作接口,不能操作类)
接口中有且仅有一个抽象方法
Lambda 带参数无返回值 1、代码示例:
接口中有且只有一个方法,方法有一个参数,无返回值
采用匿名内部类和 Lambda 表达式分别进行实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class StringHandlerDemo { public static void main (String[] args) { useStringHandler(new StringHandler() { @Override public void printMessage (String msg) { System.out.println("我是匿名内部类" + msg); } }); useStringHandler((String msg)->{System.out.println("我是Lambda表达式" + msg);}); useStringHandler( (msg) -> System.out.println("我是Lambda表达式" + msg)); useStringHandler( msg -> System.out.println("我是Lambda表达式" + msg)); } public static void useStringHandler (StringHandler stringHandler) { stringHandler.printMessage("coffeelize" ); } } interface StringHandler { void printMessage (String msg) ; }
Lambda 无参数有返回值 如果 Lambda 所操作的接口中的方法,有返回值,一定要通过 return 语句,否则会出现编译错误
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public class RandomNumHandlerDemo { public static void main (String[] args) { useRandomNumHandler(new RandomNumHandler() { @Override public int getNumber () { Random r = new Random(); int num = r.nextInt(10 ) + 1 ; return num; } }); useRandomNumHandler( () -> { Random r = new Random(); int num = r.nextInt(10 ) + 1 ; return num; } ); } public static void useRandomNumHandler (RandomNumHandler randomNumHandler) { int result = randomNumHandler.getNumber(); System.out.println(result); } } interface RandomNumHandler { int getNumber () ; }
Lambda 带参数带返回值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public class CalculatorDemo { public static void main (String[] args) { useCalculator(new Calculator() { @Override public int calc (int a, int b) { return a + b; } }); useCalculator((int a, int b)->{ return a + b; }); useCalculator( (a,b) -> a + b ); } public static void useCalculator (Calculator calculator) { int result = calculator.calc(10 ,20 ); System.out.println(result); } } interface Calculator { int calc (int a, int b) ; }
我们可以看到,以上代码中,第二种 Lambda 的书写更加简练,关于省略规则请往下看
1 2 3 4 5 6 7 8 useCalculator((int a, int b)->{ return a + b; }); useCalculator( (a,b) -> a + b );
省略规则
参数类型可以省略,但是有多个参数的情况下,不能只省略一个
为什么可以省略呢?因为在接口中的方法中已经定义了参数类型,可以推导出来参数类型,所以可以省略
如果参数有且仅有一个,那么小括号可以省略
如果代码块的语句只有一条,可以省略大括号和分号,甚至是 return
若有返回参数,要省略,大括号、分号和 return 一起省略