🔒文章目录:
1.❤️❤️前言~🥳🎉🎉🎉
2.反射
2.1反射定义
2.2反射主要应用场景
2.3Class类(反射机制的起源)
2.31获取Class类
2.32Class类常用方法
2.33获得类中的成员变量
2.34使用类中的成员方法
2.35 使用类中的构造方法
2.4反射的优缺点
3.枚举
3.1枚举的语法
3.2 枚举常用方法
3.3枚举的构造方法
3.4 枚举与反射
3.5总结
4.Lambda表达式
4.1什么是Lambda表达式?
4.2匿名内部类的回顾
4.3 Lambda表达式进行简化
4.4Lambda表达式进行简化的要求
4.5Lambda 表达式省略规则
4.6匿名内部类的变量捕获
4.7Lambda表达式的优缺点
5.总结
1.❤️❤️前言~🥳🎉🎉🎉
Hello, Hello~ 亲爱的朋友们👋👋,这里是E绵绵呀✍️✍️。
如果你喜欢这篇文章,请别吝啬你的点赞❤️❤️和收藏📖📖。如果你对我的内容感兴趣,记得关注我👀👀以便不错过每一篇精彩。
当然,如果在阅读中发现任何问题或疑问,我非常欢迎你在评论区留言指正🗨️🗨️。让我们共同努力,一起进步!
加油,一起CHIN UP!💪💪
🔗个人主页:E绵绵的博客
📚所属专栏:1. JAVA知识点专栏
深入探索JAVA的核心概念与技术细节
2.JAVA题目练习
实战演练,巩固JAVA编程技能
3.c语言知识点专栏
揭示c语言的底层逻辑与高级特性
4.c语言题目练习
挑战自我,提升c语言编程能力
📘 持续更新中,敬请期待❤️❤️
2.反射
2.1反射定义
Java反射是Java语言的一种特性,它允许程序在运行时自我检查并对内部成员进行操作。这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。具体来说,反射机制允许在运行状态中
对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意方法和属性,并且能改变它的属性。
Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。其本质是JVM得到Class对象之后,再通过Class对象进行反编译,从而获取对象的各种信息。
2.2反射主要应用场景
- 在运行时判断任意一个对象所属的类;
- 在运行时实例化任意一个类的对象;
- 在运行时获取任意类的名称、package信息、所有属性、方法、注解、类型、类加载器等;
- 在运行时获取任意对象的属性,并且能改变对象的属性;
- 在运行时调用任意对象的方法。
2.3Class类(反射机制的起源)
2.31获取Class类
其中有三种方法:
- Class.forName("类的全路径");
- 类名.class
- 对象名.getClass();
而当获取到class类时再用其获取原本的类的对象实例我们就可以用
newInstance() //创建一个类的实例
代码如下:
public class Test { public static void main(String[] args) throws Exception{ //方法1:通过 Class.forName("类的全路径");获取Class Class<?> class1 = Class.forName("Student"); //方法2:通过 类名.class 获取Class Class<?> class2 = Student.class; //方法3:通过 对象名.getClass(); 获取Class Student student = new Student("E棉", "35", 17); Class<?> class3 = student.getClass(); try{ Student student1 = (Student) class3.newInstance(); }catch (Exception e) {} } } class Student{ private String name; private String number; public String sex; public int age; public Student(String name, String number, int age) { this.name = name; this.number = number; this.age = age; } public Student(String name){ this.name = name; } private Student(String name, String number){ this.name = name; this.number = number; } private void schooling(){ System.out.println("去上学"); } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getNumber() { return number; } public void setNumber(String number) { this.number = number; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", number='" + number + '\'' + ", sex='" + sex + '\'' + ", age=" + age + '}'; } }
这里我们出现了三个现象:
1.对于forname其实有编译时异常ClassNotFoundException这个隐患,所以需要用throws将其抛出。
2. newInstans方法的返回值是T,所以被擦除成了Object,要进行强制类型转换
3. newInstans方法会抛出 InstantiationException, IllegalAccessException两个异常,所以需要用try catch将其捕获,因为会出现异常,所以我们很少使用它。一般我们都是用constructor,newinstance(参数)去生成对应的类,这样就不会出现异常,对于该知识点我们在后面会讲到
这里涉及异常知识点,如果不懂可以看我的异常文章,讲的很清楚
异常第一部分
异常第二部分
2.32Class类常用方法
调用Class的方法一般会返回三种类型的对象
- Field对象:存放获取对象的成员变量属性
- Method对象:存放获取对象的方法
- Constructor对象:存放获取对象的构造方法
下面是Class类的一些常用方法
获取成员变量
getFields() //获取所有公开的成员变量(返回一个数组)getDeclaredFields() //获取所有本类定义的成员变量,包括私有(返回一个数组)
getField(变量名) //获取指定公共属性的Field对象
getDeclaredField(变量名) //获取指定包括私有,
获取成员方法
getMethods() //获取所有公开的方法(返回一个数组)getMethod(方法名,参数类型列表) //获取指定公开方法的Method对象
getDeclaredMethods() //获取所有本类定义的的方法,包括私有(返回一个数组)
getDeclaredMethod(方法名,int.class,String.class) //获取指定包括私有
获取构造方法
getConstructor(参数类型列表) //获取公开的构造方法
getConstructors() //获取所有的公开的构造方法(返回一个数组)
getDeclaredConstructors() //获取所有的构造方法,包括私有(返回一个数组)
getDeclaredConstructor(int.class,String.class) //获取指定包括私有
2.33获得类中的成员变量
这里我们获取一个类中的私有成员变量并对其进行修改。
public class Test2 { public static void main(String[] args) throws NoSuchFieldException,ClassNotFoundException,IllegalAccessException{ Class<?> studentClass = Class.forName("Student"); Field field = studentClass.getDeclaredField("name"); field.setAccessible(true); Student student=new Student("lq","102",18); field.set(student, "max"); System.out.println(student); } } class Student{ private String name; private String number; public String sex; public int age; public Student(String name, String number, int age) { this.name = name; this.number = number; this.age = age; } public Student(String name){ this.name = name; } private Student(String name, String number){ this.name = name; this.number = number; } private void schooling(){ System.out.println("去上学"); } public String getName() { return name; } private void setName(String name) { this.name = name; } public String getNumber() { return number; } public void setNumber(String number) { this.number = number; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", number='" + number + '\'' + ", sex='" + sex + '\'' + ", age=" + age + '}'; } }
需要注意的是,如果我们需要修改私有变量的话,就要多加一个代码:
field.setAccessible(true);
2.34使用类中的成员方法
这里我们使用一个类中的私有成员方法。
public class Test3 { public static void main(String[] args) throws NoSuchMethodException,InvocationTargetException, IllegalAccessException,ClassNotFoundException { Class<?> studentClass = Class.forName("Student"); Method method = studentClass.getDeclaredMethod("setName", String.class); method.setAccessible(true); Student student=new Student("lq","102",18); method.invoke(student, "max"); System.out.println(student); } } class Student{ private String name; private String number; public String sex; public int age; public Student(String name, String number, int age) { this.name = name; this.number = number; this.age = age; } public Student(String name){ this.name = name; } private Student(String name, String number){ this.name = name; this.number = number; } private void schooling(){ System.out.println("去上学"); } public String getName() { return name; } private void setName(String name) { this.name = name; } public String getNumber() { return number; } public void setNumber(String number) { this.number = number; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", number='" + number + '\'' + ", sex='" + sex + '\'' + ", age=" + age + '}'; } }
同理,需要注意的是,如果我们需要使用私有方法的话,就要多加一个代码:
method.setAccessible(true);
2.35 使用类中的构造方法
这里我们使用一个类中的私有构造方法,跟前面大不相同。
public class Test4 { public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException,IllegalAccessException,ClassNotFoundException,InstantiationException{ Class<?> studentClass = Class.forName("Student"); Constructor constructor = studentClass.getDeclaredConstructor(String.class, String.class); constructor.setAccessible(true); Student student= (Student) constructor.newInstance("lq","102"); System.out.println(student); } } class Student{ private String name; private String number; public String sex; public int age; public Student(String name, String number, int age) { this.name = name; this.number = number; this.age = age; } public Student(String name){ this.name = name; } private Student(String name, String number){ this.name = name; this.number = number; } private void schooling(){ System.out.println("去上学"); } public String getName() { return name; } private void setName(String name) { this.name = name; } public String getNumber() { return number; } public void setNumber(String number) { this.number = number; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", number='" + number + '\'' + ", sex='" + sex + '\'' + ", age=" + age + '}'; } }
同理私有时依旧需要该方法
constructor.setAccessible(true);
因为用该构造方法不会抛出异常,只会存在异常的隐患,所以相较于之前的直接用class.newInstance()会抛出异常,我们更倾向于用这个。
2.4反射的优缺点
优点:
1. 对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法
2. 增加程序的灵活性和扩展性,降低耦合性,提高自适应能力
3. 反射已经运用在了很多流行框架如:Struts、Hibernate、Spring 等等。
缺点:
1. 使用反射会有效率问题。会导致程序效率降低。具体参考 相关文章
2. 反射技术绕过了源代码的技术,因而会带来维护问题。反射代码比相应的直接代码更复杂
3.枚举
3.1枚举的语法
在 Java 中,可以通过关键字
enum
来定义枚举类型。枚举类型的定义格式如下:enum EnumName { Constant1, Constant2, Constant3, ... }
其中
EnumName
是枚举类型的名称,Constant1
、Constant2
、Constant3
等是枚举类型的常量,这些常量就是枚举类型创建的实例,枚举常量的名称通常采用大写字母命名,多个单词之间用下划线分隔。对于枚举内部除了以上部分,其他的跟正常类是一样的,都可以有成员方法和成员变量
在 Java 中,可以通过枚举类型的名称来访问枚举常量。例如,假设有一个名为 Weekday 的枚举类型,可以通过如下方式来访问枚举常量:
Weekday monday = Weekday.Monday;
这里的 Weekday.Monday 表示 Weekday 枚举类型中的 Monday 常量,因为该常量为Weekday的实例,所以可以接收
枚举类型可以定义方法,这些方法可以在枚举常量上调用。例如,可以在
Weekday
枚举类型中定义一个isWeekend
方法来判断当前枚举常量是否为周末:enum Weekday { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday; public boolean isWeekend() { return this == Saturday || this == Sunday; } }
在上面的例子中,通过在枚举常量后面定义方法,可以在每个枚举常量上调用这个方法。例如,可以通过
Saturday.isWeekend()
来判断Saturday
是否为周末,因为枚举常量是枚举实例。
枚举类型本身就是一个类,其默认继承java.lang.Enum,也就是说,自己写的枚举类,就算没有显示的继承Enum ,但是其默认继承这个类。
3.2 枚举常用方法
1. name()
返回枚举常量的名称Color color = Color.RED; String name = color.name(); System.out.println(name);// 输出:RED
2.valueOf(String name)
根据枚举常量名称,返回枚举实例 Color color = Color.valueOf("RED"); System.out.println(color.name()); // 输出:RED
3.values()
返回枚举类所有的枚举常量(数组形式)Color[] colors = Color.values(); for (Color color : colors) { System.out.println(color.name()); } */ 输出: RED GREEN BLUE */
4.ordinal()
返回此枚举常量的序数(其枚举声明中的位置,其中初始常量被分配一个序号零)Color color = Color.RED; int ordinal = color.ordinal(); System.out.println(ordinal);//输出:0
5.compareTo()
比较两个枚举值常量序数之差Color color = Color.RED; int n = color.compareTo(Color.BLUE) ;//0 减 2 System.out.println(n);
3.3枚举的构造方法
枚举类型在Java中可以包含构造方法,用于初始化枚举常量(所以说枚举常量是实例)。枚举的构造方法必须是私有的,因为枚举常量在定义时就被实例化,并且不能在其他地方进行实例化。
这些构造方法只能在枚举常量的定义中被调用,并且只能用来初始化枚举常量的值。当枚举常量有参数后,需要提供相应的构造方法。
enum Weekday { Monday("星期一"), Tuesday("星期二"), Wednesday("星期三"), Thursday("星期四"), Friday("星期五"), Saturday("星期六"), Sunday("星期日"); private String value; private Weekday(String value) { this.value = value; } public String getValue() { return value; } }
3.4 枚举与反射
我们知道反射对于任何一个类,哪怕其构造方法是私有的,我们也可以通过反射拿到他的实例对象,那么枚举的构造方法也是私有的,我们是否可以拿到呢?
这里我们直接公布答案
根据Java源代码, newInstance() 方法在创建实例之前会先检查目标类是否是一个枚举类型。如果目标类是枚举类型, newInstance() 方法会抛出 InstantiationException 异常,阻止通过反射去创建枚举实例。
所以说我们无法通过反射去使用枚举的构造方法,也可以知道枚举它是特别安全的。
3.5总结
1、枚举本身就是一个类,其构造方法默认为私有的,且都是默认继承 java.lang.Enum
2、枚举可以避免反射和序列化问题
3、枚举的优点:
枚举常量更简单安全
枚举具有内置方法 ,代码更优雅
4.枚举的缺点:
不可继承,无法扩展
4.Lambda表达式
4.1什么是Lambda表达式?
Lambda 表达式是一种匿名函数,它可以在代码中以简洁的方式表示一个功能或行为。Lambda 表达式通常由参数列表、箭头符号和函数体组成。参数列表定义了该函数接受的输入参数,箭头符号表示函数体的开始,函数体则定义了函数的具体实现。Lambda 表达式可以作为一个整体被传递给其他函数或方法,也可以直接被调用。它跟匿名内部类有关。
4.2匿名内部类的回顾
既然跟匿名内部类相关,那我们先来回顾匿名内部类。
匿名内部类是一种在Java中定义匿名类的方式。它允许在创建对象的同时定义该对象的类,并且不需要为该类单独命名。通常情况下,我们需要先定义一个类,然后才能创建该类的对象。但是在某些场景下,我们只需要定义一个临时的类来实现某个接口或继承某个类的功能,这时就可以使用匿名内部类。简单的理解就是减少了新类型的出现。
public class Test2 {
public static void main(String[] args) {
Animal cat = new Animal(){
@Override
public void fun() {
System.out.println("是只猫");
}
};
Animal dog = new Animal() {
@Override
public void fun() {
System.out.println("是只狗");
}
};
cat.fun();
dog.fun();
}
}
interface Animal{
public void fun();
}
以上就是匿名内部类的例子,而我们之后要用Lambda表达式对其进行简化。
4.3 Lambda表达式进行简化
public class Test2 {
public static void main(String[] args) {
Animal cat = () -> {
System.out.println("是只猫");
};
Animal dog = () -> {
System.out.println("是只狗");
};
cat.fun();
dog.fun();
}
}
interface Animal{
public void fun();
}
所以Lambdab 表达式存在的意义就是简化匿名内部类,具体格式为 (被重写方法的形参列表)-> {(被重写方法的方法体)} ;
4.4Lambda表达式进行简化的要求
并不是所有的匿名内部类都可以用 Lambda 表达式进行简化的,只有函数式接口才可以被 Lambda 表达式简化。
函数式接口是指只包含一个抽象方法的接口。在Java中,函数式接口是支持函数式编程的基础,它可以用作 Lambda 表达式的目标类型。
Java 8引入了
@FunctionalInterface
注解,用于标识一个接口为函数式接口。编译器会检查被标注的接口是否符合函数式接口的定义,如果不符合,编译器会报错。总的来说,如果是接口并且只包含了一个抽象方法,或者引用了
@FunctionalInterface
注解的时候都为函数式接口,都可以被 Lambda 表达式简化。
4.5Lambda 表达式省略规则
我们通过以上的讲解,现在知道可以通过 Lambda 表达式进行简化,那么还能不能更进一步简化呢?
答案肯定是可以的,可以继续简化。
具体来介绍省略的规则:
1. 参数类型可以省略不写,但名称要一模一样。
2. 如果只有一个参数的,参数类型可以省略不写,同时()也可以不写。
3. 如果重写的方法体只有一行代码,可以省略大括号不写,同时要省略分号!此时,如果这行代码是 return 语句,也必须去掉 return 语句。
以下我们给个例子去直观显示:
public class Test {
public static void main(String[] args) {
double[] arr = new double[]{1.0,2.0,9,4.0,5.0};
//第一代的样子
/* Arrays.setAll(arr, new IntToDoubleFunction() {
@Override
public double applyAsDouble(int value) {
return arr[value] * 0.8;
}
});*/
//第二代的样子
/* Arrays.setAll(arr, (int value) ->{
return arr[value] * 0.8;
});*/
//第三代的样子
Arrays.setAll(arr,value ->arr[value] * 0.8 );
System.out.println(Arrays.toString(arr));
}
}
所以这就是我们的Lambda表达式省略规则。
4.6匿名内部类的变量捕获
对于匿名内部类中出现的变量,要么必须用final修饰,要么其是变量但从初始化后从未修改过一次。否则就不能用到匿名内部类中,这就是变量捕获。
下面给个例子。
以下是正确代码,可以执行
class Test {
public void func() {
System.out.println("func()");
}
}
public class TestDemo {
public static void main(String[] args) {
int a = 100;
Test test = new Test() {
@Override
public void func() {
System.out.println("我是内部类,且重写了func这个方法!");
System.out.println("我是捕获到变量 a == " + a
+ " 我是一个常量,或者是一个没有改变过值的变量!");
}
};
test.func();
}
}
如下代码是错误代码,不能执行,编译错误
class Test {
public void func() {
System.out.println("func()");
}
}
public class TestDemo {
public static void main(String[] args) {
int a = 100;
Test test = new Test() {
@Override
public void func() {
a = 99;
System.out.println("我是内部类,且重写了func这个方法!");
System.out.println("我是捕获到变量 a == " + a
+ " 我是一个常量,或者是一个没有改变过值的变量!");
}
};
test.func();
}
}
4.7Lambda表达式的优缺点
Lambda表达式的优点很明显,在代码层次上来说,使代码变得非常的简洁。缺点也很明显,代码不易读。
优点:
1. 代码简洁,开发迅速
2. 方便函数式编程
3. 非常容易进行并行计算
4. Java 引入 Lambda,改善了集合操作
缺点:
1. 代码可读性变差
2. 在非并行计算中,很多计算未必有传统的 for 性能要高
3. 不容易进行调试
5.总结
所以这篇文章就这样结束了,下篇文章将带来泛型的进阶,讲述之前没讲过的泛型知识点。
在此,我们诚挚地邀请各位大佬们为我们点赞、关注,并在评论区留下您宝贵的意见与建议。让我们共同学习,共同进步,为知识的海洋增添更多宝贵的财富!🎉🎉🎉❤️❤️💕💕🥳👏👏👏