🎉🎉🎉点进来你就是我的人了
博主主页:🙈🙈🙈戳一戳,欢迎大佬指点!
人生格言:当你的才华撑不起你的野心的时候,你就应该静下心来学习!欢迎志同道合的朋友一起加油喔🦾🦾🦾
目标梦想:进大厂,立志成为一个牛掰的Java程序猿,虽然现在还是一个🐒嘿嘿
谢谢你这么帅气美丽还给我点赞!比个心
目录
1. 反射的理解
2. 反射相关的类
2.1 Class类中的相关方法编辑
2.2 反射使用
2.3 反射优缺点
3.枚举的使用
3.1 背景及定义
3.2 使用
3.3 枚举优点缺点
4.枚举不能被反射
5. Lambda表达式
5.1 Lambda表达式语法
5.2 函数式接口
5.3 Lambda表达式的基本使用
5.4 变量捕获
5.5 Lambda在集合中使用
5.6 Lambda表达式优缺点
1. 反射的理解
(1)反射机制(reflection)
java的反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能调用它的任意方法和属性,既然可以拿到,那就可以修改部分类型信息;这种动态获取信息以及动态调用方法的功能称为java语言的反射机制
(2)应用
1.反射最重要的作用是开发各种通用的框架
2.在日常的第三方应用开发的过程中,经常遇到某个类的某个成员变量、方法或是私有的或是只对系统应用开发的,这时候就可以用java反射机制获取私有成员或方法
(3)反射基本信息
java程序中许多对象运行时会有两种类型:
运行时类型和编译时类型
程序运行本质是在运行时发现对象的类的真实信息,通过反射机制就可以判断出该对象和类属于那些类
2. 反射相关的类
类名 | 用途 |
---|---|
Class类 | 代表类的实体,在运行java应用程序中表示类的接口 |
Field类 | 代表类的成员变量 / 类的属性 |
Method类 | 代表类的方法 |
Constructor类 | 代表类的构造方法 |
2.1 Class类中的相关方法
(1)常用获得类相关的方法
方法 | 用途 |
---|---|
getClassLoader() | 获得类的加载器 |
getDeclaredClasses() | 返回一个数组,数组中包含该类中所有类和接口类的对象(包括私有) |
forName(String className) | 根据类名返回类的对象 |
newInstance() | 创建类的实例 |
getName() | 获得类的完整路径名字 |
(2)常用获得类中属性相关的方法(返回值为Field相关)
方法 | 用途 |
---|---|
getField(String name) | 获得某个公有的属性对象 |
getFields() | 获得所有公有的属性对象 |
getDeclaredField(String name) | 获得某个属性对象 |
getDeclaredFields() | 获得所有属性对象 |
(3)获得类中的构造器相关的方法(返回值为Constructor相关)
方法 | 用途 |
---|---|
getConstructor(Class...<?>parameter Types) | 获得该类中与参数类型匹配的公有构造方法 |
getConstructors() | 获得该类的所有公有构造方法 |
getDeclaredConstructor(Class...<?> parameterTypes) | 获得该类中与参数类型匹配的构造方法 |
getDeclaredConstructors() | 获得该类所有构造方法 |
(4)获得类中方法相关的方法(返回值为Method相关)
方法 | 用途 |
---|---|
getMethod(String name, Class...<?> parameterTypes) | 获得该类某个公有的方法 |
getMethods() | 获得该类所有公有的方法 |
getDeclaredMethod(String name, Class...<?> parameterTypes) | 获得该类某个方法 |
getDeclaredMethods() | 获得该类所有方法 |
2.2 反射使用
(1)获得Class对象的三种方式
这里我们已获取Person为例
class Person {
int age;
String name;
public Person() {
}
public Person(int age, String name) {
this.age = age;
this.name = name;
}
}
1. 使用Class.forName("类的全路径名");静态方法
public static void main(String[] args) throws ClassNotFoundException { //1.通过Class对象的forName()静态方法获取(用的最多,注意抛异常) Class<?> p1 = Class.forName("Person"); }
2. 使用.class方法
//3.直接通过 类型.class获取(更安全可靠,程序性能更高) Class<?> p2 = Person.class;
3. 使用类对象的getClass()方法
//2.通过getClass获取class对象 Person person= new Person(); Class<?> p3 = Person.getClass();
我们来验证一下这三种方式获取的类对象
package test1;
public class Main {
public static void main(String[] args) throws ClassNotFoundException {
Person person = new Person();
Class<?> p1 = Class.forName("test1.Person");
Class<?> p2 = Person.class;
Class<?> p3 = person.getClass();
System.out.println(p1.equals(p2));
System.out.println(p1.equals(p3));
System.out.println(p2.equals(p3));
}
}
(2)反射的使用
所有和反射相关的包都在import.java.lang.reflect包下
1.通过反射创建对象
public static void reflectNewInstance() {
try {
Class<?> c1 = Class.forName("reflectdemo.Student");
Student student = (Student) c1.newInstance();
System.out.println(student);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
2.反射获取私有的构造方法
public static void reflectPrivateConstructor() {
try {
Class<?> c1 = Class.forName("reflectdemo.Student");
//注意传入对应的参数
Constructor<?> constructor =
c1.getDeclaredConstructor(String.class, int.class);
//你确定要在类外访问私有方法吗.设置为true后可修改访问权限
constructor.setAccessible(true);
Student student = (Student) constructor.newInstance("xiangyu",22);
System.out.println(student);
} catch (ClassNotFoundException e) {
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
3.反射获取私有属性
public static void reflectPrivateField() {
try {
Class<?> c1 = Class.forName("reflectdemo.Student");
Student student = (Student) c1.newInstance();
//可以修改该属性的值
Field field = c1.getDeclaredField("name");
//访问私有的,提前询问一下是否确定访问
field.setAccessible(true);
field.set(student,"liubang");
System.out.println(student);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
4. 反射获取私有方法
public static void reflectPrivateMethod() {
try {
Class<?> c1 = Class.forName("reflectdemo.Student");
Student student = (Student) c1.newInstance();
Method method = c1.getDeclaredMethod("function", String.class);
method.setAccessible(true);
method.invoke(student,"通过反射给你传参");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
2.3 反射优缺点
优点:
1.任意一个类,都能够知道这个类的所有属性和方法
任意一个对象,都能够调用它的任意一个方法
2.增加程序的灵活性和扩展性,降低耦合性,提高自适应能力
3.反射运用在很多框架上
缺点:
1.反射因为调用很多方法,就会有效率降低的问题
2.反射技术绕过了源代码的技术,会有维护的问题,并且反射代码比相应直接代码更复杂
3.枚举的使用
3.1 背景及定义
枚举是在JDK1.5以后引入的。主要用途是:将一组常量组织起来,在这之前表示一组常量通常使用定义常量的方式:
public static int final RED = 1;
public static int final GREEN = 2;
public static int final BLACK = 3;
但是常量举例有不好的地方,例如:可能碰巧有个数字1,但是他有可能误会为是RED,现在我们可以直接用枚举来进行组织,这样一来,就拥有了类型,枚举类型。而不是普通的整形1.
public enum TestEnum {
RED,BLACK,GREEN;
}
优点:将常量组织起来统一进行管理
场景:错误状态码,消息类型,颜色的划分,状态机等等…
本质:是 java.lang.Enum 的子类,也就是说,自己写的枚举类,就算没有显示的继承 Enum ,但是其默认继承了这个类。
3.2 使用
1、switch语句
public enum TestEnum {
RED,BLACK,GREEN,WHITE;
public static void main(String[] args) {
TestEnum testEnum2 = TestEnum.BLACK;
switch (testEnum2) {
case RED:
System.out.println("red");
break;
case BLACK:
System.out.println("black");
break;
case WHITE:
System.out.println("WHITE");
break;
case GREEN:
System.out.println("black");
break;
default:
break;
}
}
}
2、常用方法
Enum 类的常用方法
方法名称 | 描述 |
---|---|
values() | 以数组形式返回枚举类型的所有成员 |
ordinal() | 获取枚举成员的索引位置 |
valueOf() | 将普通字符串转换为枚举实例 |
compareTo() | 比较两个枚举成员在定义时的顺序 |
示例一:
public enum TestEnum {
RED,BLACK,GREEN,WHITE;
public static void main(String[] args) {
TestEnum[] testEnum2 = TestEnum.values();
for (int i = 0; i < testEnum2.length; i++) {
System.out.println(testEnum2[i] + " " + testEnum2[i].ordinal());
}
System.out.println("=========================");
System.out.println(TestEnum.valueOf("GREEN"));
}
}
示例二:
public enum TestEnum {
RED,BLACK,GREEN,WHITE;
public static void main(String[] args) {
//拿到枚举实例BLACK
TestEnum testEnum = TestEnum.BLACK;
//拿到枚举实例RED
TestEnum testEnum21 = TestEnum.RED;
System.out.println(testEnum.compareTo(testEnum21));
System.out.println(BLACK.compareTo(RED));
System.out.println(RED.compareTo(BLACK));
}
}
刚刚说过,在Java当中枚举实际上就是一个类。所以我们在定义枚举的时候,还可以这样定义和使用枚举:
重要:枚举的构造方法默认是私有的
public enum TestEnum {
RED("red", 1), BLACK("black", 2), WHITE("white", 3), GREEN("green", 4);
private String name;
private int key;
/**
* 1、当枚举对象有参数后,需要提供相应的构造函数
* 2、枚举的构造函数默认是私有的 这个一定要记住
*
* @param name
* @param key
*/
private TestEnum(String name, int key) {
this.name = name;
this.key = key;
}
public static TestEnum getEnumKey(int key) {
for (TestEnum t : TestEnum.values()) {
if (t.key == key) {
return t;
}
}
return null;
}
public static void main(String[] args) {
System.out.println(getEnumKey(2));
}
}
3.3 枚举优点缺点
优点:
1.枚举常量更简单安全
2.枚举具有内置方法,代码跟美观
3.枚举无法被反射和序列化
缺点:
1.不可继承,无法扩展
4.枚举不能被反射
前面学过了反射,对任何一个类,即使构造方法是私有的,也可以通过反射拿到实例对象,那么枚举的构造方法也是私有的,通过反射可以拿到吗?
package test1;
import java.lang.reflect.Constructor;
public enum TestEnum {
RED("red", 1), BLACK("black", 2), WHITE("white", 3), GREEN("green", 4);
private String name;
private int key;
/**
* 1、当枚举对象有参数后,需要提供相应的构造函数
* 2、枚举的构造函数默认是私有的 这个一定要记住
*
* @param name
* @param key
*/
private TestEnum(String name, int key) {
this.name = name;
this.key = key;
}
public static TestEnum getEnumKey(int key) {
for (TestEnum t : TestEnum.values()) {
if (t.key == key) {
return t;
}
}
return null;
}
public static void reflectPrivateConstructor() {
try {
Class<?> classStudent = Class.forName("test1.TestEnum");
//注意传入对应的参数,获得对应的构造方法来构造对象,当前枚举类是提供了两个参数分别是String和int
Constructor<?> declaredConstructorStudent =
classStudent.getDeclaredConstructor(String.class, int.class);
//设置为true后可修改访问权限
declaredConstructorStudent.setAccessible(true);
Object objectStudent = declaredConstructorStudent.newInstance("绿色", 666);
TestEnum testEnum = (TestEnum) objectStudent;
System.out.println("获得枚举的私有构造函数:" + testEnum);
} catch (Exception ex) {
ex.printStackTrace();
}
}
public static void main(String[] args) {
reflectPrivateConstructor();
}
}
输出结果:
异常信息是: java.lang.NoSuchMethodException: TestEnum.(java.lang.String,int) ,什么意思是:就是没有对应的构造方法,我的天呐!我们提供的枚举的构造方法就是两个参数分别是String 和 int 啊!!!!问题出现在哪里呢?还记不记得我们说过的,我们所有的枚举类,都是默认继承与java.lang.Enum ,说到继承,继承了什么?继承了父类除构造函数外的所有东西,并且子类要帮助父类进行构造!而我们写的类,并没有帮助父类构造!那意思是,我们要在自己的枚举类里面,提供super吗?不是的,枚举比较特殊,虽然我们写的是两个,但是默认他还添加了两个参数,哪两个参数呢?我们看一下Enum类的源码:
protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
也就是说,我们自己的构造函数有两个参数一个是String一个是int,同时他默认后边还会给两个参数,一个是String一个是int。也就是说,这里我们正确给的是4个参数:
public static void reflectPrivateConstructor() {
try {
Class<?> classStudent = Class.forName("test1.TestEnum");
//注意传入对应的参数,获得对应的构造方法来构造对象,当前枚举类是提供了两个参数分别是String和int
Constructor<?> declaredConstructorStudent =
classStudent.getDeclaredConstructor(String.class, int.class, String.class, int.class);
//设置为true后可修改访问权限
declaredConstructorStudent.setAccessible(true);
//这里为了凑齐参数,后两个参数随便给,不给也行,默认为空值
Object objectStudent = declaredConstructorStudent.newInstance("绿色", 666, "父类参数", 888);
TestEnum testEnum = (TestEnum) objectStudent;
System.out.println("获得枚举的私有构造函数:" + testEnum);
} catch (Exception ex) {
ex.printStackTrace();
}
}
此时运行程序结果是:
报错了,不过这次就是我想要的结果!此时的异常信息显示,是我的一个方法这个方法是:
newInstance() 报错了!没错,问题就是这里,我们来看一下这个方法的源码,为什么会抛出
java.lang.IllegalArgumentException: 异常呢?
源码显示:
问题显示枚举在这个地方被过滤掉了,也就是说我们并不能通过反射来获取枚举类的实例!!!所以枚举是很安全的,枚举可以避免反射和序列化问题
这道题是2017年阿里巴巴曾经问到的一个问题,不看不知道,一看吓一跳!同学们记住这个坑。原版问题是:为什么枚举实现单例模式是安全的?希望同学们记住这个问题!
5. Lambda表达式
5.1 Lambda表达式语法
(1)理解
Lambda表达式相当于匿名函数一样,Lambda表达式允许将一个函数作为另外一个函数的参数;
Lambda表达式允许通过表达式来代替功能接口;
Lambda表达式就和方法一样,提供一个正常的参数列表和使用这些参数的主体(可以是表达式、代码块)
lambda表达式也可称为闭包
(2)语法
语法:(parameters)-> expression 或 (parameters) -> {statements;}
Lambda表达式三部分组成:
1) paraments:相当于方法中的参数列表,这里的参数是函数式接口里的函数
参数类型可以明确声明,也可以又jvm隐含判断
当只有一个推断类型时可以省略圆括号
2)->:相当于 “被用于”
3)方法体:表达式可以是代码块,也可以是函数式接口里方法的实现
可以有返回值,也可以不返回
//1.不需要参数,直接返回值
()-> 6
//2.一个参数类型,返回值
x -> 2*x
//3.两个参数,返回值
(x,y) -> x+y
//4.两个int型整数,返回值
(int x,int y) -> x*y
//5.接收一个string对象,在控制台打印,不返回值
(String s) -> system.out.print(s)
5.2 函数式接口
函数式接口定义:一个接口有且只有一个抽象方法 。
我们在写函数式接口时可以声明@FunctionalInterface 注解,这样编译器就可以根据函数式接口的定义来要求该接口,此时如果有两个抽象方法,编译器就会报错
public class Demo01 { public static void main(String[] args) { PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o2-o1; } }); } //此时这里相当于一个类实现了Comparator接口 同时重写了抽象方法compare PriorityQueue<Integer> priorityQueue = new PriorityQueue<>((o1, o2) -> {return o2-o1;}); PriorityQueue<Integer> priorityQueue1 = new PriorityQueue<>(((o1, o2) -> o2-o1)); }
5.3 Lambda表达式的基本使用
Lambda 表达式本质上是一个匿名函数
可以理解为: Lambda 就是匿名内部类的简化
实际上是创建了一个类,实现了接口,重写了接口的方法
下面来写几个接口,分别用Lambda表达式和不使用Lambda表达式来调用对比一下
(1)无返回值无参数
@FunctionalInterface
interface NoParameterNoReturn {
void test();
}
两种方式分别调用对比
public static void main(String[] args) {
NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn() {
@Override
public void test() {
System.out.println("测试一下");
}
};
noParameterNoReturn.test();
NoParameterNoReturn noParameterNoReturn1 = () -> {System.out.println("测试一下");};
NoParameterNoReturn noParameterNoReturn2 = () -> System.out.println("测试一下");
NoParameterNoReturn noParameterNoReturn3 = () -> {
System.out.println("测试1下");
System.out.println("测试2下");
};
}
(2) 无返回值一个参数
@FunctionalInterface
interface OneParameterNoReturn {
void test(int a);
}
public static void main(String[] args) {
OneParameterNoReturn oneParameterNoReturn = (int x) -> {
System.out.println(x);
};
oneParameterNoReturn.test(100);
System.out.println("简化:");
OneParameterNoReturn oneParameterNoReturn1 = x -> System.out.println(x);
OneParameterNoReturn oneParameterNoReturn2 = System.out::println;
oneParameterNoReturn1.test(10);
}
(3)无返回值多个参数
@FunctionalInterface
interface MoreParameterNoReturn {
void test(int a,int b);
}
public static void main(String[] args) {
MoreParameterNoReturn moreParameterNoReturn = (int a,int b) -> {
System.out.println(a+b);
};
moreParameterNoReturn.test(10,20);
//要求两个类型同时省略
MoreParameterNoReturn moreParameterNoReturn1 = (a,b) -> System.out.println(a+b);
moreParameterNoReturn1.test(20,30);
}
(4)有返回值无参数
@FunctionalInterface
interface NoParameterReturn {
int test();
}
public static void main(String[] args) {
NoParameterReturn noParameterReturn = () -> {return 10;};
int ret = noParameterReturn.test();
System.out.println(ret);
NoParameterReturn noParameterReturn1 = () ->10;
int ret2 = noParameterReturn1.test();
System.out.println(ret2);
}
(5)有返回值一个参数
@FunctionalInterface
interface OneParameterReturn {
int test(int a);
}
public static void main(String[] args) {
OneParameterReturn oneParameterReturn = x -> 2*x;
System.out.println(oneParameterReturn.test(10));
}
(6)有返回值多个参数
@FunctionalInterface
interface MoreParameterReturn {
int test(int a,int b);
}
public static void main(String[] args) {
MoreParameterReturn moreParameterReturn = (x,y) -> x+y;
System.out.println(moreParameterReturn.test(10, 20));
}
Lambda语法简略规则:
1. 参数类型可以省略,并且要省略就要全部省略
2. 参数的括号当里面只有一个参数时,括号可以省略
3. 如果方法体中只有一句代码,那么大括号可以省略
4. 如果方法体中只有一句代码,尤其是return语句,那么大括号和return关键字都可以省略
5.4 变量捕获
(1)匿名内部类变量捕获
class Demo {
void fun() {
System.out.println("dasd");
}
}
在匿名内部类中,变量捕获是指外部变量的捕获,
外部变量在匿名内部类中使用,这个变量要么是被final修饰,如果不是被final修饰的 你要保证在使用之前,没有被修改
public static void main7(String[] args) {
int a = 10;
//在匿名内部类中,变量捕获(外部变量的捕获),看使用的外部变量一定是未被修改过的
new Demo() {
@Override
void fun() {
int c = 99;
c = 100;
System.out.println(c+"重写一下" + a);
}
}.fun();
}
(2)Lambda变量捕获
Lambda 表达式中存在变量捕获 ,必须理解Lambda变量捕获后,才可以明白Lambda 表达式的作用域,其变量捕获规则和匿名内部类规则类似
5.5 Lambda在集合中使用
集合中新增了部分接口,便于和Lambda表达式对接
这里举几个例子
(1)Collection接口中forEach()打印全部
(2)List接口中sort()排序
分别写出使用Lambda表达式和不使用的方法
public static void main(String[] args) {
//Lambda内部也是匿名内部类,用外部变量不能被修改
ArrayList<String> list = new ArrayList<>();
list.add("Hello");
list.add("bit");
list.add("hellol");
list.add("lambda");
//打印全部
list.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
list.forEach(s -> System.out.println(s));
//排序
list.sort(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
list.sort(((o1, o2) -> o1.compareTo(o2)));
list.forEach(s -> System.out.println(s));
}
5.6 Lambda表达式优缺点
优点:
1.代码简介,开发迅速
2.方便函数式变成
3.改善集合操作
缺点:
1.代码可读性差
2.不容易进行调试
3.在非并行计算中,很多计算未必有传统for性能高