反射 枚举

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
 对于任意一个对象,都能够调用它的任意一个方法和属性;
 这种动态获取类的信息以及动态调用对象的方法的功能称为java语言的反射机制。
 要想解剖一个类, 必须先要获取到该类的字节码文件对象。
 而解剖使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类型的对象

反射概述以及获取class文件对象的三种方式

获取一个类对应的字节码文件对象:

package com.itheima.demo1.demo7;

public class Student {
    public String name;
    protected int age;
    char sex;
    private double height;

}
package com.itheima.demo1.demo7;

import java.lang.reflect.Field;

public class Test {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException {
        //要使用反射机制,你先要获取到该类的字节码文件对象,然后通过该类的字节码文件对象,去剖析类的构成,
        //以及通过反射,去调用类中的属性和功能。

        //获取一个类的的字节码文件对象的三种方式。
        // //方式1.getClass();
        Student student = new Student();
        //Student.class---->会为这个Student.class文件来创建一个Class对象。
        Class<? extends Student> aClass = student.getClass();
        //方式2 每一个类,都一个 .class 属性,就可以获取该类的字节码文件对象。
        Class<Student> aClass1 = Student.class;

        //方式3:通过Class 类中的静态方法 forName();
        //全限定名:包名+类名 org.westos.demo.Student
        Class<?> aClass2 = Class.forName("com.itheima.demo1.demo7.Student");
        //System.out.println(aClass==aClass1);//true
       // System.out.println(aClass1==aClass2);//true
        //获取指定的字段对象
        //获取公共的
        Field name = aClass2.getField("name");
        System.out.println(name);//public java.lang.String com.itheima.demo1.demo7.Student.name
        //过去各种类型的都可
        Field height = aClass2.getDeclaredField("height");
        System.out.println(height);
        //通过反射给字段设置值

        Object o = aClass2.newInstance();//创建一个设置值的对象
        name.set(o,"cherry");//cherry
        System.out.println(name.get(o));
        //给私有字段设置值,取消私有的语法检查
        height.setAccessible(true);
        height.setDouble(o,1.68);

        System.out.println(height.getDouble(o));//1.68


    }
}

package com.itheima.demo1.demo8;

public class Student {
    private Student() {

    }

    public void show()
    {
        System.out.println("show方法调用了");
    }

    protected void show2(String name)
    {
        System.out.println("show2方法调用了" + name);
    }


    void show3(String name, int age) {

        System.out.println("show3方法调用了" + name + "===" + age);
    }

    String show4(String name, int age) {
        System.out.println("show4方法调用了" + name + "===" + age);
        return "返回值";
    }

    private void test(String name, int age) {

        System.out.println("私有的方法调用了" + name + "==" + age);
    }
}
通过反射越过泛型检查
package com.itheima.demo1.demo8;

import java.lang.reflect.Method;

public class Test {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
        Class<?> aClass = Class.forName("com.itheima.demo1.demo8.Student");
        //getMethods(); 获取所有的公共的方法对象,包括父类的公共方法。
        Method[] methods = aClass.getMethods();
        for (Method method : methods) {
           // System.out.println(method);//打印出公共类的地址和原始父类的地址值
        }
        //getDeclaredMethods(); 获取所有的方法对象,包括私有的方法。不获取父类的方法对象。
        Method[] methods1 = aClass.getDeclaredMethods();
        for (Method method : methods1) {
            //System.out.println(method);
        }

        //获取指定的方法对象的地址
       Method show = aClass.getMethod("show");
       // System.out.println(show);
        Method show2 = aClass.getDeclaredMethod("show2", String.class);
      //  System.out.println(show2);



    }
}

反射是在运行期反射

泛型在编译器有效,在运行期就被擦出了。

//需求:我这个集合的泛型声明为了String类型,但是我想在集合中添加Integer类型的元素
//通过反射机制,越过了泛型检测。
ArrayList<String> list = new ArrayList<>();
list.add("aaaa");
list.add("bbbb");
//泛型只在编译期有效,在运行期就擦除了.
//list.add(100);
Class<? extends ArrayList> aClass = list.getClass();
Method add = aClass.getMethod("add", Object.class);
add.invoke(list, 100);
System.out.println(list);
//public void setProperty(Object obj, String propertyName, Object value){},
 //    此方法可将obj对象中名为propertyName的属性的值设置为value。

 Student student = new Student();
 MyUtils.setProperty(student, "name", "张三");
 Object name = MyUtils.getProperty(student, "name");
 System.out.println(name);
public static void setProperty(Object obj, String propertyName, Object value) throws NoSuchFieldException, IllegalAccessException {
    Class<?> aClass = obj.getClass();
    Field declaredField = aClass.getDeclaredField(propertyName);//获取指定的字段对象
    declaredField.setAccessible(true);// 值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。
    declaredField.set(obj, value);
}

public static Object getProperty(Object obj, String propertyName) throws NoSuchFieldException, IllegalAccessException {
    Class<?> aClass = obj.getClass();
    Field declaredField = aClass.getDeclaredField(propertyName);
    declaredField.setAccessible(true);
    Object o = declaredField.get(obj);
    return o;
}
 //类 Proxy 代理类,可以通过反射机制,帮你创建换一个代理对象。
/* newProxyInstance(ClassLoader loader, Class < ? >[]interfaces, InvocationHandler h)
 返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。*/
 UserDao userDao = new UserDaoImpl();
 //userDao.insert();
 //userDao.update();
 //userDao = new UserDaoStaticClass();
 //userDao.insert();
 UserDao proxy = ProxyUtils.getProxy(userDao);
 proxy.insert();
 // proxy.update();
 //  proxy.delete();
 //  proxy.query();
 System.out.println("===================");
 proxy.update();

 //JDK动态代理:要求:必须要有接口。
 //CGLIB动态代理:不需要有接口。
动态代理
  • 在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)
    B:案例演示:	动态代理的实现
    我们可以通过Proxy类中的静态方法获取一个代理对象:
    
    - 
    - public static Object newProxyInstance(ClassLoader loader,  Class<?>[] interfaces,  InvocationHandler h)
    
loader: 			类加载器
interfaces:			接口对应的一个Class数组
InvocationHandler:	这个其实就是要代理对象所做的事情的一个类的封装
动态代理:
         *  特点:字节码随用随创建,随用随加载
         *  作用:不修改源码的基础上对方法增强
         *  分类:
         *      基于接口的动态代理
         *      基于子类的动态代理
         *  基于接口的动态代理:
         *      涉及的类:Proxy
         *      提供者:JDK官方
         *  如何创建代理对象:
         *      使用Proxy类中的newProxyInstance方法
         *  创建代理对象的要求:
         *      被代理类最少实现一个接口,如果没有则不能使用
         *  newProxyInstance方法的参数:
         *      ClassLoader:类加载器
         *          它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法。
         *      Class[]:字节码数组 
         *          它是用于让代理对象和被代理对象有相同方法。固定写法。
         *      InvocationHandler:用于提供增强的代码
         *          它是让我们写如何代理。我们一般都是写一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。
         *          此接口的实现类都是谁用谁写。
new InvocationHandler() {
                    /**
                     * 作用:执行被代理对象的任何接口方法都会经过该方法
                     * 方法参数的含义
                     * @param proxy   代理对象的引用
                     * @param method  当前执行的方法
                     * @param args    当前执行方法所需的参数
                     * @return        和被代理对象方法有相同的返回值
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    }
                }
//产生代理对象
/**
 *newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
 *  loader 类加载器,用的是和被代理对象同一个加载器 固定写法
 *  interfaces 代理对象所实现的所有接口的Class类型的数组 固定写法
 *
 *  InvocationHandler 接口,让你用来做增强的
 */
UserDao proxy = (UserDao) Proxy.newProxyInstance(dao.getClass().getClassLoader(), dao.getClass().getInterfaces(), new InvocationHandler() {
    @Override
    /**
     * proxy 代理对象
     * method 方法对象
     * args 方法对象的参数的数组
     */
    //方法调用会被转发到该类的invoke()方法。
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //System.out.println("invoke 方法执行了");
        Object invoke = null;
        if (method.getName().equals("insert") || method.getName().equals("update")) {
            System.out.println("权限校验的功能");
            invoke = method.invoke(dao);
            System.out.println("日志记录");
        }
        return invoke;
    }
});
return proxy; //返回代理对象

枚举

JDK1.5的新特性: 自动拆装箱 , 泛型 , 增强for  , 可变参数 , 枚举
枚举概述:	就是一个类只能存在几个固定的对象,那么这个就是枚举.我们就可以使用这些对象可以表示一些固定的值.
//方向
Direction front = Direction.FRONT;
Direction behind = Direction.BEHIND;
Direction left = Direction.LEFT;
Direction right = Direction.RIGHT;
System.out.println(front);
System.out.println(behind);
System.out.println(left);
System.out.println(right);

 //Ctrl+Shift+U 转换大小写
    public static final Direction FRONT = new Direction();
    public static final Direction BEHIND = new Direction();
    public static final Direction LEFT = new Direction();
    public static final Direction RIGHT = new Direction();

    private Direction() {
    }
//方向
Direction front = Direction.FRONT;
Direction behind = Direction.BEHIND;
Direction left = Direction.LEFT;
Direction right = Direction.RIGHT;
System.out.println(front.name);
System.out.println(behind.name);
System.out.println(left.name);
System.out.println(right.name);
System.out.println("===============================");
front.show("前");
behind.show("后");
left.show("左");
right.show("右");




public static final Direction FRONT = new Direction("前") {
        @Override
        public void show(String name) {

            System.out.println(name);
        }
    };
    public static final Direction BEHIND = 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);
        }
    };
    String name;

    private Direction(String name) {

        this.name = name;
    }

    public abstract void show(String name);
/*枚举项,多个枚举项用逗号隔开,最后一个枚举项,下面没有内容了分号可以省略不写
 * 枚举项要位于第一行
 * */
FRONT, BEHIND, LEFT, RIGHT;
int num = 0;

private Direction() {
}

private Direction(String name) {
}

一个人枚举类有一个枚举项,其实就是一单例模式的饿汉式

FRONT("前"), BEHIND("后"), LEFT("左"), RIGHT("右");
String name;
枚举的注意事项
定义枚举类要用关键字enum
	所有枚举类都是Enum的子类,但是不要显式的写出来
	枚举类的第一行上必须是枚举项,最后一个枚举项后的分号是可以省略的,但是如果枚举类有其他的东西,这个分号就不能省略。建议不要省略
	枚举类可以有构造器,但必须是private的,它默认的也是private的。枚举项的用法比较特殊:枚举(“”);
	枚举类也可以有抽象方法,但是枚举项必须重写该方法
	枚举在switch语句中的使用
枚举类的常见方法
int ordinal()  返回枚举项的序号
	int compareTo(E o) 比较两个枚举项的 返回的是两个枚举项序号的 差值
	String name() 获取枚举项的名称
	String toString()获取枚举项的名称
	<T> T valueOf(Class<T> type,String name) 用来获取指定的枚举项  参数1:枚举类对应的字节码对象 参数2 枚举项的名称
	values()  获取所有的枚举项
	此方法虽然在JDK文档中查找不到,但每个枚举类都具有该方法,它遍历枚举类的所有枚举值非常方便
B:案例演示:	枚举类的常见方法

public static void main(String[] args) {
	
	// 测试
	Direction front = Direction.FRONT ;
	Direction behind = Direction.BEHIND;
	Direction left = Direction.LEFT ;
	Direction right = Direction.RIGHT ;
	
	System.out.println(front.ordinal());
	System.out.println(behind.ordinal());
	System.out.println(left.ordinal());
	System.out.println(right.ordinal());
	
	System.out.println("----------------------------------");

	System.out.println(front.compareTo(right));
	
	System.out.println("----------------------------------");
	
	System.out.println(front.name());
	
	System.out.println("----------------------------------");
	
	System.out.println(front.toString());
	System.out.println(front);
	
	System.out.println("----------------------------------");
	
	// <T> T valueOf(Class<T> type,String name):	用来获取指定的枚举项
	// type: 表示的是对应的枚举的字节码文件对象
	// name: 就是枚举项的名称
	Direction direction = Direction.valueOf(Direction.class, "RIGHT") ;
	System.out.println(direction);
	
	System.out.println("----------------------------------");
	
	Direction[] directions = Direction.values() ;
	
	for(Direction d : directions){
		System.out.println(d);
	}

}
如果你的枚举类,只有一个枚举项,那其实就是个单列模式
public enum Student {
    S
}
public static void main(String[] args) {
        Student s = Student.S;
        Student s2 = Student.S;
        System.out.println(s == s2);
    }
   public static void main(String[] args) {
        //_ 千位分隔符
        //int money=10_000_000;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值