✨✨个人主页:沫洺的主页
📚📚系列专栏: 📖 JavaWeb专栏📖 JavaSE专栏 📖 Java基础专栏📖vue3专栏
📖MyBatis专栏📖Spring专栏📖SpringMVC专栏📖SpringBoot专栏
📖Docker专栏📖Reids专栏📖MQ专栏📖SpringCloud专栏
💖💖如果文章对你有所帮助请留下三连✨✨
🍂内部类
类中有什么
- 静态程序段(static{})
- 属性
- 方法
- 成员内部类,可以使用private修饰
- 初始化程序段(很少用)
内部类的概念
内部类:在一个类的内部定义的类
也就是在类{}里面创建的类才叫内部类
内部类的种类
- 成员内部类
- 局部内部类
- 匿名内部类(直接new 抽象类,直接new 接口)
🌳成员内部类
什么是成员内部类
首先我们知道成员变量和成员方法是类的成员,那么成员内部类就是和成员变量和成员方法同一级的一个类而已,成员内部类也就是作为这个类(外部类)的成员存在。
举个例子
package cn.moming7; public class A { //相较于B这个类而言,A是外部类 public class B{ //类种类,内部类 } }
案例分析
package cn.moming7; public class A { //外部类 public int num = 100; //成员变量 class B { //内部类 public int num = 200; //内部类的成员变量 public void mod() { //内部类的成员方法 int num = 300; //内部类的局部变量 System.out.println(num); //就近输出 System.out.println(this.num); //指定当前类下的变量num System.out.println(A.this.num); //指定外部类A下的变量num } } public static void main(String[] args){ A.B b = new A().new B(); //内部类实例化 b.mod(); //调用内部类的成员方法 } }
300 200 100
🍁局部内部类
什么是局部内部类
首先我们知道局部变量是在成员方法里的,那么局部内部类和局部变量同一级,那么局部内部类的位置就在成员方法里
举个例子
package cn.moming7; public class C { //外部类 private int age; //成员变量 public void show(){ //成员方法 String name="沫洺"; //局部变量 class B{ //局部内部类 } } }
局部内部类注意事项
局部内部类中可以使用局部变量,但是一但使用了局部变量,该局部变量就不能被重新赋值了。
因为: 当局部内部类中去使用局部变量时,局部变量就会被java自动使用final修饰。局部内部类不能被public private static修饰,但可以被abstract、final修饰。
因为局部内部类是放在代码块或方法中的,不能有访问控制修饰符,且不能用static修饰。
🌿匿名内部类
什么是匿名
匿名就是没有名字
比如下面的代码
package cn.moming7; import java.util.Random; public class Demo { public static void main(String[] args) { Random random = new Random(); //实例化对象时给new出来的对象起了一个变量名random System.out.println(random); System.out.println(random.nextInt(10)); System.out.println(new Random()); //实例化对象没有给名字,相当于new了个匿名对象 System.out.println(new Random().nextInt(10)); } }
java.util.Random@3b07d329 0 java.util.Random@41629346 9
匿名内部类的作用
匿名内部类最常用的方法就是当做一个参数来用的,简化开发
什么时候使用匿名内部类
必须存在继承和实现关系的时候才可以使用
举例
在继承关系中对父类的方法进行重写
父类(Animal)
package cn.moming8; public class Animal { public void call(){ System.out.println("动物叫"); } }
子类(Dog)
package cn.moming8; public class Dog extends Animal{ @Override public void call() { System.out.println("狗叫"); } }
测试类(Test)
package cn.moming8; public class Test { public static void main(String[] args) { Dog dog = new Dog(); dog.call(); } }
狗叫
通过匿名内部类去重写方法
类(Animal)
package cn.moming8; public class Animal { public void call(){ System.out.println("动物叫"); } }
测试类(Test)
package cn.moming8; public class Test { public static void main(String[] args) { new Animal(){ @Override public void call() { System.out.println("狗叫"); } }.call(); } }
狗叫
通过对比很明显的可以看到匿名内部类对方法重写更为简化
通过匿名内部类对数组进行排序
package moming;
import java.util.Arrays;
public class Comparator {
public static void main(String[] args) {
Integer[] ints = {3,5,8,9,1,6,5};
Arrays.sort(ints); //默认升序
System.out.println(Arrays.toString(ints));
Arrays.sort(ints, new java.util.Comparator<Integer>() { //通过匿名内部类重写比较方法
@Override
public int compare(Integer o1, Integer o2) {
return o2-o1;
}
});
System.out.println(Arrays.toString(ints));
}
}
[1, 3, 5, 5, 6, 8, 9]
[9, 8, 6, 5, 5, 3, 1]
内部类的注意事项
- 内部类可以直接使用外部类的成员(外部类就是指该类里有内部类,那么相交内部类而言它就是外部类)。
- 内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件,但是前面冠以 外部类的类名和$符号。
- 内部类不能用普通的方式访问。内部类是外部类的一个成员,因此内部类可以自由地访问外部 类的成员变量,无论是否是private的。
- 外部类不能直接访问其内部类,想要访问内部类,需实例化内部类。
- 内部类声明成静态的,就不能随便的访问外部类的成员变量了,此时内部类只能访问外部类的 静态成员变量。
- 其他类想要直接访问内部类,可直接实例化内部类,方法如下外部类名.内部类 对象名 = new 外部类().new 内部类();
- 如果内部类是静态的,当其他类调用内部类可直接通过类名调用,格式: 外部类.内部类 对象名 = new 外部类.内部类()
- 当内部类是静态的,且方法也是静态的,此时可不需实例化对象,格式:外部类.内部类.静态方法名();
🍂Lambda表达式
先来看看Lambda表达式长什么样
拿出上面的匿名内部类排序数组,自动生成Lambda表达式
切换完之后看效果发现Lambda表达式非常的简洁
那么什么是Lambda表达式
Lambda 表达式”(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得
名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函 数。Lambda表达式可以表示闭包(注意和数学传统意义上的不同)。
- Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。
- Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
- 使用 Lambda 表达式可以使代码变的更加简洁紧凑,有效避免内部匿名类出现。
Lambda表达式的格式
(形式参数)->{方法体}
(): 代表形参- 就是接口中抽象方法的参数
->: 固定语法
{}: 代表方法体- 接口中抽象方法重写之后的方法体
Lambda表达式的本质
创建了一个接口的实现类对象
使用Lambda表达式的前提
必须是一个接口并且接口中有且仅有一个抽象方法
比如上面的排序案例(Comparator)
所以根据Lambda表达式的特点 ,我们自己实现一个Lambda表达式
接口(DbDao1)无参无返回值
package cn.moming9; public interface DbDao1 { void show(); //无参无返回值 }
接口(DbDao2)有参无返回值
package cn.moming9; public interface DbDao2 { void myName(String name); //有参无返回值 }
接口(DbDao3)有参有返回值
package cn.moming9; public interface DbDao3 { int myAge(int age); //有参有返回值 }
测试类(Test)
说明:useDbDao方法就是一个工具人,Lambda表达式的作用是作为参数来用的,所以我们需要一个方法的参数类型是是接口的方法。
package cn.moming9; public class Test { public static void main(String[] args) { useDbDao1(()->{ //无参无返回值 System.out.println("你好"); }); useDbDao2((String name)->{ //有参无返回值 System.out.println(name); }); useDbDao3((int age)->{ //有参有返回值 return age; }); } public static void useDbDao1(DbDao1 d1){ d1.show(); } public static void useDbDao2(DbDao2 d2){ d2.myName("沫洺"); } public static void useDbDao3(DbDao3 d3){ d3.myAge(18); } }
你好 沫洺
🍃省略模式
- 参数类型可以省略。但是有多个参数的情况下,不能只省略一个。
- 如果参数有且仅有一个,那么小括号可以省略。
- 如果代码块的语句只有一条,可以省略大括号,分号,return。
例如上面的测试类省略后
package cn.moming9; public class Test { public static void main(String[] args) { useDbDao1(()-> System.out.println("你好")); useDbDao2((name)-> System.out.println(name)); useDbDao3((age)-> age); } public static void useDbDao1(DbDao1 d1){ d1.show(); } public static void useDbDao2(DbDao2 d2){ d2.myName("沫洺"); } public static void useDbDao3(DbDao3 d3){ d3.myAge(18); } }
Lambda和匿名内部类的区别
- 使用匿名内部类 语法上没有限制
- 使用Lambda 必须是一个接口并且接口中有且仅有一个抽象方法
🌻lambda表达式双冒号
双冒号运算符就是java中的方法引用,方法引用的格式是类名 :: 方法名。
这里只是方法名,方法名的后面没有括号“()”。--------> 这样的式子并不代表一定会调用这个方 法。这种式子一般是用作Lambda表达式,Lambda有所谓的懒加载,不要括号就是说,看情况调用方法。
以上面的测试类为例(useDbDao2使用双冒号)
package cn.moming9; public class Test { public static void main(String[] args) { useDbDao1(()-> System.out.println("你好")); //useDbDao2((name)-> System.out.println(name)); useDbDao2(System.out::println); useDbDao3((age)-> age); } public static void useDbDao1(DbDao1 d1){ d1.show(); } public static void useDbDao2(DbDao2 d2){ d2.myName("沫洺"); } public static void useDbDao3(DbDao3 d3){ d3.myAge(18); } }
例:
- 表达式:person ->person.getAge();可以替换为 Person::getAge
- 表达式:()-> new HashMap<>();可以替换为 HashMap::new
- 表达式:()->Math.random();可以替换为 Math::random
这种方法引用或者是双冒号运算对应的参数类型是Function<T,R>T表示传入的类型,R表示返回的 类型。比如表达式person -> person.getAge();传入的参数是person,返回值是peron.getAge(),那么方法引用Person::getAge就对应着Funciton<Person,Integer>类型。