内部类
内部类概念
-
在一个类中定义一个类。举例:在一个类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<Person>() {
@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表达式
理解: 对于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表达式的三要素:
-
形式参数,箭头,代码块
-
演示
Lambda表达式演示1
-
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(); } }
Lambda表达式演示2
-
演示内容:
有参无返回值抽象方法
-
代码
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("风和日丽,晴空万里"); } }
Lambda表达式演示3
-
演示内容:
有参有返回值抽象方法
-
代码(这里就用上面匿名内部类用到的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<Person>() {
@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);
}
}
}
Lambda表达式的省略模式
-
省略的规则
-
参数类型可以省略。但是有多个参数的情况下,不能只省略一个参数的类型
-
如果参数有且仅有一个,那么小括号可以省略
-
如果代码块的语句只有一条,可以省略大括号和分号及return关键字(三个要一起省,不能省一部分)
-
-
知道了这几点后,自己试着去省略上面的那些代码吧,你肯定会的,而且会发现它用着真的很爽。
Lambda表达式和匿名内部类的区别
-
所需类型不同
-
匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
-
Lambda表达式:只能是接口
-
-
使用限制不同
-
如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
-
如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式
-
-
实现原理不同
-
匿名内部类:编译之后,产生一个单独的.class字节码文件(xxx$xxx.class,内部类类名都是:外部类类名$内部类类名)
-
Lambda表达式:编译之后,没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成
-
-
思想不一样
-
匿名内部类是面向对象的思想
-
Lambda表达式是函数式思想
-
本文到此就结束了,如果有什么错误或建议欢迎到评论区留言哦~
如果本文对你有所帮助,也不要吝啬给个三连呀~