内部类概念
在一个类中定义一个类。举例:在一个类A的内部定义一个类B,类B就被称为内部类
按照内部类在类中定义的位置不同,可以分为如下两种形式
在类的成员位置:成员内部类
在类的局部位置:局部内部类
内部类的访问特点
内部类可以直接访问外部类的成员,包括私有
外部类要访问内部类的成员,必须创建对象
示例代码:
- public class Outer {
- private int num = 10;
- private class Inner {
- public void show() {
- System.out.println(num); //内部类可以直接访问外部类的成员,包括私有
- }
- }
- public void method() {
- Inner i = new Inner();
- i.show(); //外部类要访问内部类的成员,必须创建对象
- }
- }
成员内部类的定义位置
在类中方法,跟成员变量是一个位置
被 private 修饰则是 私有成员内部类,被 static 修饰则是 静态成员内部类
外界创建成员内部类格式 (私有成员内部类和静态成员内部类不能用这种方式)
格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;
Outer.Inner oi = new Outer().new Inner();
- class Outer {
- // 当为 public 时,外界创建内部类对象也是像下面这样
- class Inner {
- }
- }
-
- class Test {
- public static void main(String[] args) {
- Outer.Inner oi = new Outer().new Inner();
- }
- }
将一个类,设计为内部类的目的,大多数都是不想让外界去访问,所以内部类的定义应该私有化,私有化之后,再提供一个可以让外界调用的方法,方法内部创建内部类对象并调用。
示例代码:
- class Outer {
- private int num = 10;
- private class Inner {
- public void show() {
- System.out.println(num);
- }
- }
- public void method() {
- Inner i = new Inner();
- i.show();
- }
- }
-
- public class InnerDemo {
- public static void main(String[] args) {
- // 这样会报错
- //Outer.Inner oi = new Outer().new Inner();
- Outer o = new Outer();
- o.method();
- }
- }
创建静态成员内部类对象格式:外部类名.内部类名 对象名 = new 外部类名.内部类名();
外界调用静态成员内部类中的静态方法:外部类名.内部类名.方法名();
示例代码
- class Outer {
- static class Inner {
- public void show(){
- System.out.println("inner..show");
- }
-
- public static void method(){
- System.out.println("inner..method");
- }
- }
- }
-
- public class Test3Innerclass {
- public static void main(String[] args) {
- // 这样会报错
- //Outer.Inner oi = new Outer().new Inner();
-
- // 外部类名.内部类名 对象名 = new 外部类名.内部类名();
- Outer.Inner oi = new Outer.Inner();
- oi.show();
-
- Outer.Inner.method(); // 静态成员内部类中静态方法调用方式
- }
- }
局部内部类定义位置
局部内部类是在方法中定义的类
局部内部类方式方式
局部内部类,外界是无法直接使用,需要在方法内部创建对象并使用
该类可以直接访问外部类的成员,也可以访问方法内的局部变量
示例代码(局部内部类在自己平常写和源码中基本不会出现)
- class Outer {
- private int num = 10;
- public void method() {
- int num2 = 20;
- class Inner {
- public void show() {
- System.out.println(num);
- System.out.println(num2);
- }
- }
- Inner i = new Inner();
- i.show();
- }
- }
- public class OuterDemo {
- public static void main(String[] args) {
- Outer o = new Outer();
- o.method();
- }
- }
匿名内部类是一种特殊的局部内部类
匿名内部类的前提
存在一个类或者接口,这里的类可以是具体类也可以是抽象类
匿名内部类的格式
格式:new 类名 ( ) { 重写方法 } new 接口名 ( ) { 重写方法 }
举例:
- new Inner(){
- @Override
- public void method(){}
- }
匿名内部类的本质
本质:是一个继承了该类或者实现了该接口的子类匿名对象
匿名内部类的细节
匿名内部类可以通过多态的形式接收
- interface Inner {
- void method1();
- void method2();
- }
- class Test {
- public static void main(String[] args) {
- Inner i = new Inner() {
-
- @Override
- public void method1() {
- System.out.println("method1……");
- }
-
- @Override
- public void method2() {
- System.out.println("method2……");
- }
- };
- i.method1();
- i.method2();
-
- // 如果不以多态的形式接收,那么它后面调用其中一个方法了
- new Inner() {
-
- @Override
- public void method1() {
- System.out.println("method1……");
- }
-
- @Override
- public void method2() {
- System.out.println("method2……");
- }
- }.method1();
- }
-
- }
匿名内部类在开发中的应用场景
当发现某个方法需要,接口或抽象类的子类对象,我们就可以传递一个匿名内部类过去,来简化传统的代码。就比如我们都用到过的:Arrays.sort() 方法,我们就可以传入比较器的匿名对象来让数组按我们想要的方式排序
例子:
- class Test {
- public static void main(String[] args) {
- Person zs = new Person("张三", 28);
- Person ls = new Person("李四", 19);
- Person ww = new Person("王五", 99);
- Person[] persons = new Person[]{zs, ls, ww};
- Arrays.sort(persons, new Comparator
() { - @Override
- public int compare(Person o1, Person o2) {
- return o1.getAge() - o2.getAge();
- }
- });
- for (Person person : persons) {
- System.out.println(person);
- }
- }
- }
-
- class Person {
- private String name;
- private int age;
-
- public Person(String name, int age) {
- this.name = name;
- this.age = age;
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public int getAge() {
- return age;
- }
-
- public void setAge(int age) {
- this.age = age;
- }
-
- @Override
- public String toString() {
- return "Person{" +
- "name='" + name + '\'' +
- ", age=" + age +
- '}';
- }
- }
输出结果:

理解: 对于Lambda表达式, 相当于对匿名内部类进行了优化。但是本质上是有区别的
初步体验:
- /*
- 游泳接口
- */
- interface Swimming {
- void swim();
- }
-
- public class TestSwimming {
- public static void main(String[] args) {
- // 通过匿名内部类实现
- goSwimming(new Swimming() {
- @Override
- public void swim() {
- System.out.println("铁汁, 我们去游泳吧");
- }
- });
-
- /* 通过Lambda表达式实现
- 理解: 对于Lambda表达式, 对匿名内部类进行了优化
- */
- goSwimming(() -> System.out.println("铁汁, 我们去游泳吧"));
- }
-
- /**
- * 使用接口的方法
- */
- public static void goSwimming(Swimming swimming) {
- swimming.swim();
- }
- }
函数式编程思想概述
在数学中,函数就是有输入量、输出量的一套计算方案,也就是“拿数据做操作”
面向对象思想强调“必须通过对象的形式来做事情”
函数式思想则尽量忽略面向对象的复杂语法:“强调做什么,而不是以什么形式去做”
而我们要学习的 Lambda 表达式就是函数式思想的体现
格式:
(形式参数) -> {代码块}
形式参数:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可
->:由英文中画线和大于符号组成,固定写法。代表指向动作
代码块:是我们具体要做的事情,也就是以前我们写的方法体内容
组成Lambda表达式的三要素:
形式参数,箭头,代码块
Lambda表达式的使用前提
有一个接口
接口中有且仅有一个抽象方法
演示内容:
无参无返回值抽象方法
代码
- //接口
- public interface Eatable {
- void eat();
- }
- //实现类
- public class EatableImpl implements Eatable {
- @Override
- public void eat() {
- System.out.println("一天一苹果,医生远离我");
- }
- }
- //测试类
- public class EatableDemo {
- public static void main(String[] args) {
- //在主方法中调用useEatable方法
- Eatable e = new EatableImpl();
- useEatable(e);
-
- //匿名内部类
- useEatable(new Eatable() {
- @Override
- public void eat() {
- System.out.println("一天一苹果,医生远离我");
- }
- });
-
- //Lambda表达式
- useEatable(() -> {
- System.out.println("一天一苹果,医生远离我");
- });
- }
-
- private static void useEatable(Eatable e) {
- e.eat();
- }
- }
演示内容:
有参无返回值抽象方法
代码
- public interface Flyable {
- void fly(String s);
- }
-
- public class FlyableDemo {
- public static void main(String[] args) {
- //在主方法中调用useFlyable方法
- //匿名内部类
- useFlyable(new Flyable() {
- @Override
- public void fly(String s) {
- System.out.println(s);
- System.out.println("飞机自驾游");
- }
- });
- System.out.println("--------");
-
- //Lambda
- useFlyable((String s) -> {
- System.out.println(s);
- System.out.println("飞机自驾游");
- });
-
- }
-
- private static void useFlyable(Flyable f) {
- f.fly("风和日丽,晴空万里");
- }
- }
演示内容:
有参有返回值抽象方法
代码(这里就用上面匿名内部类用到的Arrays.sort()来演示)
- class Test {
- public static void main(String[] args) {
- Person zs = new Person("张三", 28);
- Person ls = new Person("李四", 19);
- Person ww = new Person("王五", 99);
- Person[] persons = new Person[]{zs, ls, ww};
- /*Arrays.sort(persons, new Comparator
() { - @Override
- public int compare(Person o1, Person o2) {
- return o1.getAge() - o2.getAge();
- }
- });
- */
- Arrays.sort(persons, (Person o1, Person o2) -> o1.getAge() - o2.getAge());
- for (Person person : persons) {
- System.out.println(person);
- }
- }
- }
省略的规则
参数类型可以省略。但是有多个参数的情况下,不能只省略一个参数的类型
如果参数有且仅有一个,那么小括号可以省略
如果代码块的语句只有一条,可以省略大括号和分号及return关键字(三个要一起省,不能省一部分)
知道了这几点后,自己试着去省略上面的那些代码吧,你肯定会的,而且会发现它用着真的很爽。
所需类型不同
匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
Lambda表达式:只能是接口
使用限制不同
如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式
实现原理不同
匿名内部类:编译之后,产生一个单独的.class字节码文件(xxx$xxx.class,内部类类名都是:外部类类名$内部类类名)
Lambda表达式:编译之后,没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成
思想不一样
匿名内部类是面向对象的思想
Lambda表达式是函数式思想
本文到此就结束了,如果有什么错误或建议欢迎到评论区留言哦~
如果本文对你有所帮助,也不要吝啬给个三连呀~