反射与枚举
反射概述
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性,这种动态获取类的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类, 必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类型的对象。可以通过反射机制,去调用类中的任何一个属性和方法。
new 对象 调用方法或属性私有的属性和功能你外界调用不到。要使用反射机制,那么就要先获取一个类的字节码文件对象(Class 字节码文件类型)。
获取一个类的字节码文件对象的三种方式:
方式1:就是通过 Object类中的 getClass()方法
Student student = new Student(); //Student.class----->字节码文件对象
Class<? extends Student> aClass = student.getClass();
Class<? extends Student> aClass1 = student.getClass();
System.out.println(aClass==aClass1);
方式2:通过Class 中的静态方法 forName()方法来获取一个类的字节码文件对象
Class<?> aClass2 = Class.forName("org.westos.demo.Student");
Class<?> aClass3 = Class.forName("org.westos.demo.Student");
System.out.println(aClass2 == aClass3);
Class<?> aClass4 = Class.forName("org.westos.demo2.Student");
System.out.println(aClass2 == aClass4);
方式3:针对每个类,都有一个静态的.class 属性 来获取一个 类的字节码文件对象。
Class<Student> studentClass = Student.class;
Class<Object> objectClass = Object.class;
Class<String> stringClass1 = String.class;
Class<String> stringClass2 = String.class;
System.out.println(stringClass1==stringClass2);
通过反射机制,来剖析一个类的构成,一个类的构成包括:构造方法,成员变量 ,成员方法。
通过反射来剖析这个类的三个构成部分,那么这三个构成,也会看做一种类型:
构造方法 Constructor
成员变量 Field
成员方法 Method
通过反射,来调用构造方法执行,创建出该类对象
通过反射获取无参构造方法并使用
获取所有构造方法
public Constructor<?>[] getConstructors() 获取所有的构造方法不包含私有的
public Constructor<?>[] getDeclaredConstructors() 获取所有的构造方法 包括私有的
获取单个构造方法
public Constructor getConstructor(Class<?>… parameterTypes) 获取单个的构造方法 不包含私有的
public Constructor getDeclaredConstructor(Class<?>… parameterTypes) 获取单个的构造方法包含私有的
public class Student {
//定义构造方法
public Student() {
System.out.println("空参构造执行了");
}
public Student(String name) {
System.out.println("一个参数的构造执行了");
}
private Student(String name,int age) {
System.out.println("私有的构造执行了");
}
}
//1.先获取该类的字节码文件对象
Class<?> aClass = Class.forName("demo.Student"); //Student.class
//2.可以获取Student类的构造方法对象
//获取该类中所有的构造方法对象,私有的构造方法对象获取不到
Constructor<?>[] constructors = aClass.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
//获取该类中所有的构造方法对象,包括私有的构造方法
Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
System.out.println("===================================");
//获取单个的构造方法对象
//获取空参的构造方法对象
Constructor<?> constructor = aClass.getConstructor();
System.out.println(constructor);
System.out.println("===========================");
//获取一个参数的构造方法对象 参数就是,你构造方法的参数类型的字节码类型
Constructor<?> constructor1 = aClass.getConstructor(String.class);
System.out.println(constructor1);
System.out.println("===========================");
//获取私有的构造方法对象 getDeclaredConstructor()
Constructor<?> constructor2= aClass.getDeclaredConstructor(String.class,int.class);
System.out.println(constructor2);
}
}
通过反射来创建一个类的对象
public class MyTest2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//我们通过反射的方式来创建一个类的对象。
Class<?> aClass = Class.forName("demo.Student");
//获取空参的构造方法对象
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
//通过构造方法对象中的方法,创建出Student类对象
Student o = (Student) declaredConstructor.newInstance();
System.out.println(o);
System.out.println("============================");
//使用反射的方式,通过有参构造来创建对象
Constructor<?> constructor = aClass.getConstructor(String.class);
//调用构造方法对象中的方法来创建Student类的对象
Object obj = constructor.newInstance("李四");
System.out.println(obj);
System.out.println("=============================");
//通过私有构造方法来创建对象,以前的方式,创建不了
// Student student = new Student("王五", 25);
//反射这种方式,即便你是私有构造,也能创建出该类的对象
//获取私有的构造方法对象
Constructor<?> declaredConstructor1 = aClass.getDeclaredConstructor(String.class, int.class);
//取消语法检查不然会报错 因为私有的外界不能直接访问
declaredConstructor1.setAccessible(true);//值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。
Object obj2 = declaredConstructor1.newInstance("赵六", 26);
Student stu= (Student) obj2;
System.out.println(stu);
// Student.class----->Class 中的getConstructor()----->Constructor构造方法对象----> newInstance()
}
}
使用反射的方式,调用空参构造来创建对象
public class Teacher {
public Teacher(){
System.out.println("空参构造调用了");
}
}
public class MyTest {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//1.以前的方式,借助空参构造创建该类对象
//Teacher teacher = new Teacher();
//2.使用反射的方式,调用空参构造来创建对象的方式1
Class<?> aClass = Class.forName("org.westos.demo3.Teacher");
//3.获取空参构造方法对象
Constructor<?> constructor = aClass.getConstructor();
//4.调用构造方法对象中的方法来创建该类对象
Teacher teacher = (Teacher) constructor.newInstance();
System.out.println(teacher);
System.out.println("===========================");
//方式2:如果你只是想要通过空参构造来创建对象,你可以使用Class类里面的newInstance()方法直接创建对象。
Teacher teacher1 = (Teacher) aClass.newInstance();
}
}
如果你通过反射来剖析一个类的构成,对于类中的成员变量 使用Field类型来描述成员变量
public class Dog {
public String name;
public int age;
private char sex;
private Dog(){}
}
//1.你要使用反射,第一步先获取该类的字节码文件对象。
Class<?> aClass = Class.forName("org.westos.demo4.Dog");
//2.获取Dog类中的所有字段对象,但是私有的字段对象获取不到
Field[] fields = aClass.getFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("==================================");
//获取Dog类中的所有字段对象,包括私有的字段对象
Field[] fields2 = aClass.getDeclaredFields();
for (Field field : fields2) {
System.out.println(field);
}
System.out.println("=============================");
//获取单个的非私有的字段对象,你要获取哪个字段对象,你名字传入
Field fieldName = aClass.getField("name");
System.out.println(fieldName);
Field fieldAge = aClass.getField("age");
System.out.println(fieldAge);
System.out.println("====================");
//获取单个的私有字段或非私有字段的对象
Field fieldSex= aClass.getDeclaredField("sex");
System.out.println(fieldSex);
}
}
public class MyTest2 {
public static void main(String[] args) throws Exception {
//我们通过反射的方式,给类中的成员变量设置值,以及获取值。
//1.你要使用反射,第一步先获取该类的字节码文件对象。
Class<?> aClass = Class.forName("org.westos.demo4.Dog");
//2.给name字段设置,我们就要获取到name字段对象
Field fieldName = aClass.getField("name");
//调用Field类中的方法来设置值
//通过反射,创建一个类的对象
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
Object obj = declaredConstructor.newInstance();
//set()方法要两个参数,第一个参数,你该类的对象,第二个就是你要设置的具体的值
fieldName.set(obj,"小黑");
//获取name字段的值
Object o = fieldName.get(obj);
System.out.println(o);
System.out.println("==============================");
Field fieldAge = aClass.getField("age");
fieldAge.setInt(obj,100);
int anInt = fieldAge.getInt(obj);
System.out.println(anInt);
System.out.println("===================================");
//给私有字段设置值
Field fieldSex = aClass.getDeclaredField("sex");
//对于私有的,我们可以取消语法检查
fieldSex.setAccessible(true);
fieldSex.setChar(obj,'女');
//获取
char aChar = fieldSex.getChar(obj);
System.out.println(aChar);
//以前的方式--->new 一个对象--->对象.成员变量名
//反射的方式 Dog.class---->Class 类中的getField()--->Field 对象----通过Field 对象中的set() get()方法给字段设置值,以及获取字段的值
}
}
通过反射获取无参无返回值成员以及有参有返回值成员方法并使用
public class Student {
private Student() {
}
public void hehe(){
System.out.println("呵呵");
}
public void haha(String name) {
System.out.println(name+"哈哈");
}
public void test(String name,int age) {
System.out.println(name + "test"+age);
}
public String test2(String name, int age) {
System.out.println(name + "test2" + age);
return "abc";
}
private String show(String name, int age) {
System.out.println(name + "show" + age);
return "AAA";
}
}
public class MyTest {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
//对应类中的成员方法Java提供Method这个类来描述
//1.获取该类的字节码文件对象
Class<?> aClass = Class.forName("org.westos.demo.Student");
//2.获取类中所有的成员方法对象,获取到非私有的方法对象,包括从父类继承下来的方法对象也能够获取到
Method[] methods = aClass.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("==================================");
//获取所有的方法对象,包括私有的,但是不获取父类的方法对象
Method[] declaredMethods = aClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
System.out.println("=================================");
//获取单个的非私有方法对象
Method heheMethod = aClass.getMethod("hehe");
System.out.println(heheMethod);
System.out.println("========================");
//获取有参的方法对象 getMethod()方法的参数1:方法名 参数2:你方法上形参的数据类型的Class类型
Method haha = aClass.getMethod("haha", String.class);
System.out.println(haha);
System.out.println("==========================");
Method test = aClass.getMethod("test", String.class, int.class);
System.out.println(test);
System.out.println("============================");
//获取单个的私有方法对象
Method show = aClass.getDeclaredMethod("show", String.class, int.class);
System.out.println(show);
}
}
使用反射的方式,调用方法执行
//现在,我们使用反射的方式,调用方法执行
Class<?> aClass = Class.forName("org.westos.demo.Student");
//获取hehe方法对象
Method heheMethod = aClass.getMethod("hehe");
//Object invoke(Object obj, Object... args)
// 对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
Object obj = declaredConstructor.newInstance();
//invoke(obj); //参数1,就是该类的对象
Object invoke = heheMethod.invoke(obj);
System.out.println("============================");
//调用有参方法执行
Method hahaMethod = aClass.getMethod("haha", String.class);
hahaMethod.invoke(obj,"老师");
System.out.println("=========================");
Method testMethod = aClass.getMethod("test", String.class, int.class);
testMethod.invoke(obj,"王五",25);
System.out.println("==========================");
Method test2Method = aClass.getMethod("test2", String.class, int.class);
//返回的就是test2()调用完毕之后的返回值
Object invoke1 = test2Method.invoke(obj, "赵六", 26);
System.out.println(invoke1);
System.out.println("===========================");
//调用私有的方法执行
Method showMethod= aClass.getDeclaredMethod("show", String.class, int.class);
//取消私有的检查
showMethod.setAccessible(true);
//如果这个方法,有返回值,我们也可以获取
Object invoke2 = showMethod.invoke(obj, "ccc", 30);
System.out.println(invoke2);
}
}
通过反射运行配置文件内容
在开发过程中中途需要改变需求
public class Cat {
public void eat(){
System.out.println("猫吃鱼");
}
}
public class Dog {
public void eat() {
System.out.println("狗吃肉");
}
}
public class MyTest {
public static void main(String[] args) throws Exception {
Properties properties = new Properties();
properties.load(new FileReader("MyConfig.properties"));
Class<?> myClass = Class.forName(properties.getProperty("className"));
Object obj = myClass.getDeclaredConstructor().newInstance();
//调用猫类中的eat方法执行
Method methodName = myClass.getDeclaredMethod(properties.getProperty("methodName"));
Object invoke = methodName.invoke(obj);
}
}
通过反射越过泛型检查
泛型,只在编译期有效,运行期就擦除了。
public class MyTest {
public static void main(String[] args) throws Exception {
ArrayList<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
//list.add(200);
//我们通过反射机制,越过泛型检测
Class<? extends ArrayList> aClass = list.getClass();
//获取add方法对象
Method addMethod = aClass.getDeclaredMethod("add", Object.class);
//调用add方法执行。
addMethod.invoke(list,200);
addMethod.invoke(list, true);
System.out.println(list);
}
}
通过反射写一个通用的设置某个对象的某个属性为指定的值
public class Teacher {
private Teacher() {
}
private String name;
private int age;
}
public class MyTest {
public static void main(String[] args) throws Exception {
//通过反射写一个通用的设置某个对象的某个属性为指定的值
Constructor<?> declaredConstructor = Class.forName("org.westos.demo4.Teacher").getDeclaredConstructor();
declaredConstructor.setAccessible(true);
Object obj = declaredConstructor.newInstance();
MyUtils.setProperty(obj,"name","张三");
Object name = MyUtils.getProperty(obj, "name");
System.out.println(name);
System.out.println("=======================");
MyUtils.setProperty(obj, "age", 30);
Object r = MyUtils.getProperty(obj, "age");
Integer num= (Integer) r;
System.out.println(num);
}
}
动态代理的概述和实现
在不改动原有代码的基础上,对功能进行增强。我们可以使用一种更加灵活的方式 叫做动态代理
在Java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象。JDK提供的代理只能针对接口做代理。
我们有更强大的代理cglib,Proxy类中的方法创建动态代理类对象
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
最终会调用InvocationHandler的方法
InvocationHandler Object invoke(Object proxy,Method method,Object[] args)
public interface UserDao {
public abstract void add();
public abstract void delete();
public abstract void upate();
public abstract void query();
}
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("增加一个用户");
}
@Override
public void delete() {
System.out.println("删除一个用户");
}
@Override
public void upate() {
System.out.println("修改一个用户");
}
@Override
public void query() {
System.out.println("查询一个用户");
}
}
public class UserDaoStaticProxy implements UserDao{
private UserDaoImpl userDao=new UserDaoImpl();
@Override
public void add() {
System.out.println("权限的校验");
userDao.add();
}
@Override
public void delete() {
System.out.println("权限的校验");
userDao.delete();
}
@Override
public void upate() {
System.out.println("权限的校验");
userDao.upate();
}
@Override
public void query() {
System.out.println("权限的校验");
userDao.query();
}
}
public class BookDao {
public void add(){
System.out.println("权限校验");
System.out.println("增加图书");
}
}
public class MyTest2 {
public static void main(String[] args) {
// userDao 目标对象
UserDao userDao = new UserDaoImpl();
//代理对象:Java提供了一个Proxy类,可以帮你生成一个代理对象。
//Proxy 提供用于创建动态代理类和实例的静态方法
//创建一个代理对象的方法
/* static Object newProxyInstance (ClassLoader loader, Class < ?>[]interfaces, InvocationHandler h)
返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
*/
//获取出代理对象
UserDao proxy = ProxyUtils.getProxy(userDao);
proxy.add();
System.out.println("=====================");
proxy.delete();
System.out.println("=====================");
proxy.upate();
System.out.println("=====================");
proxy.query();
//注意:JDK的动态代理,必须要有一个接口。
//如果一个普通的类,并没有一个接口,你想要对这个类中的功能,进行增强,可以使用CGLIB代理
}
}
public class ProxyUtils {
//传过来的是被代理对象(目标对象)
public static UserDao getProxy(UserDao dao){
// Proxy 提供用于创建动态代理类和实例的静态方法
/* static Object newProxyInstance (ClassLoader loader, Class < ?>[]interfaces, InvocationHandler h)
返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。*/
//参数1:ClassLoader loader 类加载器 加载代理对象,跟目标对象用的是同一个加载器 固定写法
//参数2:接口对应的一个Class数组 代理对象需要实现的接口 固定写法
// InvocationHandler 是代理对象的调用处理程序 实现的接口。需要我们自己编写的那些增强的代码
UserDao obj= (UserDao) Proxy.newProxyInstance(dao.getClass().getClassLoader(), dao.getClass().getInterfaces(), new InvocationHandler() {
//Object proxy 代理对象
//Method method 被代理接口中的方法对象
//Object[] args 被代理接口中方法对象的参数数组
//当我们调用接口中的方法的时候,都会经过invoke()方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//System.out.println("invoke方法调用了");
/* System.out.println("权限校验");
Object result = method.invoke(dao);//调用目标对象的方法执行。
System.out.println("日志的记录");*/
//对某个方法进行增加
Object result=null;
if (method.getName().equals("add")) {
System.out.println("权限校验");
result = method.invoke(dao);//调用目标对象的方法执行。
System.out.println("日志的记录");
}else{
result = method.invoke(dao);//调用目标对象的方法执行。
}
return result;
}
});
return obj;//返回代理对象
}
}
JDK1.5的新特性回归以及自己实现枚举类
JDK1.5的新特性: 自动拆装箱 , 泛型 , 增强for , 可变参数 , 枚举
枚举概述: 就是一个类只能存在几个固定的对象,那么这个就是枚举.我们就可以使用这些对象可以表示一些固定的值.
public class Direction {
public static final Direction BEFORE=new Direction();
public static final Direction AFTER = new Direction();
public static final Direction LEFT = new Direction();
public static final Direction RIGHT = new Direction();
private Direction(){}
}
public class MyTest {
public static void main(String[] args) {
//方向:前后左右
Direction before = Direction.BEFORE;
Direction after = Direction.AFTER;
Direction left = Direction.LEFT;
Direction right = Direction.RIGHT;
System.out.println(before);
System.out.println(after);
System.out.println(left);
System.out.println(right);
}
}
自定义枚举类
public abstract class Direction {
public static final Direction BEFORE=new Direction("前"){
@Override
public void show(String name) {
System.out.println(name);
}
};
public static final Direction AFTER = new Direction("后") {
@Override
public void show(String name) {
System.out.println(name);
}
};
public static final Direction LEFT = new Direction("左") {
@Override
public void show(String name) {
System.out.println(name);
}
};
public static final Direction RIGHT = new Direction("右") {
@Override
public void show(String name) {
System.out.println(name);
}
};
// private Direction(){}
public String name;
private Direction(String name) {
this.name=name;
}
//提供了一个抽象方法
public abstract void show(String name);
}
public class MyTest {
public static void main(String[] args) {
Direction before = Direction.BEFORE;
Direction after = Direction.AFTER;
Direction left = Direction.LEFT;
Direction right = Direction.RIGHT;
before.show("前");
after.show("后");
left.show("左");
right.show("右");
}
}
JDK1.5之后引入了一个enum这个关键字,用来表示定义枚举。
public enum Direction {
//枚举项:必须位于第一行 最后一个枚举项可以使用;分号结束
BEFORE,AFTER,LEFT, RIGHT;
String name;
//枚举里面的构造方法,必须是私有修饰的,默认也是私有的
private Direction() {
}
}
public class MyTest {
public static void main(String[] args) {
//自定义枚举,写法太繁琐,JDK1.5之后引入了一个enum这个关键字,用来表示定义枚举。
Direction after = Direction.AFTER;
Direction before = Direction.BEFORE;
Direction left = Direction.LEFT;
Direction right = Direction.RIGHT;
System.out.println(after.toString());
System.out.println(before);
System.out.println(left);
System.out.println(right);
//JDK1.5之后,switch语句支持枚举类型
switch (Direction.RIGHT){
case LEFT:
System.out.println("左");
break;
case RIGHT:
System.out.println("右");
break;
case BEFORE:
System.out.println("前");
break;
case AFTER:
System.out.println("后");
break;
}
//使用枚举类型做判断
if(Direction.RIGHT.toString().equals("RIGHT")){
System.out.println("进来了");
}
//获取枚举项的编号
System.out.println(before.ordinal());
System.out.println(after.ordinal());
System.out.println(before.toString());
System.out.println(before.name());
//JDK1.7 支持千位分隔符
int num=100_201_411;
}
}
// Direction 枚举类 extends Enum 但是你不要显式的写出来
public enum Direction {
//枚举项
BEFORE("前"),AFTER("后"),LEFT("左"), RIGHT("右");
public String name;
//注意:枚举里面的 构造方法默认是私有的
private Direction(String name) {
this.name = name;
}
枚举类的常见方法
枚举类的常见方法
int ordinal() 返回枚举项的序号
int compareTo(E o) 比较两个枚举项的 返回的是两个枚举项序号的差值
String name() 获取枚举项的名称
String toString()获取枚举项的名称
T valueOf(Class type,String name) 用来获取指定的枚举项 参数1:枚举类对应的字节码对象 参数2 枚举项的名称
values() 获取所有的枚举项
此方法虽然在JDK文档中查找不到,但每个枚举类都具有该方法,它遍历枚举类的所有枚举值非常方便