用代码介绍java反射的使用

java反射笔记

反射的作用

放射可以使实现动态编译。好处是使代码更加灵活,减少开发中重复代码的编写。
本文主要讲解在编程中如何使用放射机制,并没涉及放射的原理

Class类

Java中,一切皆为对象,那么在程序里的类本身是不是对象呢?答案为是的,在Java中有这样专门的一个类java.lang.Class,在程序中所写的每一个类都既有着自己的对象,同时它自己本身也是Class类的对象。那么获取Class类不就能获取类的所有信息了吗

获取Class对象有三种方法

class Demo{ }
public class Main {
    public static void main(String[] args) {
        //方法一通过Object的getClass方法获取Class
        Demo demo = new Demo();
        Class<?> demoClass1 = demo.getClass();
        //方法二通过类名获取
        Class<?> demoClass2 = Demo.class;
        //方法三通过Class类的forName(类的全路径)方法
        try {
            Class.forName("com.company.Demo");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

通过Class对象输出类的信息

class Demo{ }
public class Main {
    public static void main(String[] args) {
        Class<?> clazz = Demo.class;
        System.out.println(clazz);
        //获取类的路径
        System.out.println(clazz.getName());
        //获取类的名称
        System.out.println(clazz.getSimpleName());
//        输出结果
//        class com.company.Demo
//        com.company.Demo
//        Demo
    }
}

通过Class对象获取类的实例化对象

class Demo{
    Demo(){
        System.out.println("demo被实例化了");
    }
}
public class Main {
    public static void main(String[] args) {
        Class<?> clazz = Demo.class;
        try {
            //JDK1.8及更低的版本可以直接 clazz.newInstance();
            //此时的demo对象可以和通过new关键字获取的对象一样了
            Demo demo = (Demo) clazz.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    //输出结果
    //demo被实例化了
}

构造方法的调用

class Demo{
    Demo(){
        System.out.println("===无参构造===");
    }
    Demo(String name){
        System.out.println("有参构造:参数是->"+name);
    }
}
public class Main {
    public static void main(String[] args) {
        Class<?> clazz = Demo.class;
        try {
            //调用无参构造
            Constructor<?> constructor1 = clazz.getDeclaredConstructor();
            constructor1.newInstance();
            //调用有参构造,String.class为参数的类型的Class对象多个参数用逗号分开
            Constructor<?> constructor2 = clazz.getDeclaredConstructor(String.class);
            //参数是要传的值
            constructor2.newInstance("小明");
        } catch (Exception e) {
            e.printStackTrace();
        }
        //输出结果
        //===无参构造===
        //有参构造:参数是->小明
    }
}

通过Class对象调用类中方法

为什么不直接用Class对象获取的实例化对象来调用呢?
因为同过实例化对象打点调用方法就无法实现动态调用了。看下次就会明白。先看看怎么通过Class对象调用类的方法

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
class Demo{
    public void myMethodOne(){
        System.out.println("执行Demo无参方法");
    }
    public String myMethodTow(String name){
        System.out.println("执行Demo有参方法");
        return "有惨方法的放回值->"+name;
    }
}
public class Main {
    public static void main(String[] args) {
        Class<?> clazz = Demo.class;
        try {
            //先获取类的实例化对象->这里不需要转换会Demo
            //千万不要通过强制转换回Demo直接调用。那就不是反射了。那就和直接new一个对象无区别了
            Object obj = clazz.getDeclaredConstructor().newInstance();
            System.out.println("=========执行无参===========");
            // clzz.getDeclaredMethods();获取本类的方法集合(所有类型)
            //clzz.getMethods();获取所有的(public)方法包过父类的(public)
            //clazz.getDeclaredMethod("myMethodOne");在本类中匹配一个方法名为myMethodOne的方法(修饰符无限制)
            //clazz.getMethod("myMethodOne");在本类以及父类中匹配一个方法名为myMethodOne的公共(public)方法
            Method myMethodOne = clazz.getDeclaredMethod("myMethodOne");
            //执行myMethodOne方法。参数是该类的实例化对象
            myMethodOne.invoke(obj);
            System.out.println("=========执行有参===========");
            //和无参的区别就是后面多了个参数类型的Class对象
            Method myMethodTow = clazz.getDeclaredMethod("myMethodTow",String.class);
            //和无参的区别就是后面多了个参数的值
            Object name = myMethodTow.invoke(obj, "小明");
            System.out.println(name);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //输出结果
        //=========执行无参===========
        //执行Demo无参方法
        //=========执行有参===========
        //执行Demo有参方法
        //有惨方法的放回值->小明
    }
}

操作类中的成员(变量)

import java.lang.reflect.Field;
class Demo{
    private String username;
    public String work;
    protected Integer age;

}
public class Main {
    public static void main(String[] args) {
        Class<?> clazz = Demo.class;
        try {
            Object obj = clazz.getDeclaredConstructor().newInstance();
            //clazz.getDeclaredFields();获取本类成员类型(所有修饰符public private 。。。。)
            //clazz.getFields();获取所有成员包过父类(public)
            //clazz.getDeclaredField("username");在本类中查找名字为username的成员
            Field usernameField = clazz.getDeclaredField("username");
            //打破原有的封装->这里因为username是私有的所以要打破原有的封装
            //是的私有的类型不能访问只是对普通的new方法有限制
            usernameField.setAccessible(true);
            //usernameField.getType();获取成员类型的Class类再通过getName获取类型的名字
            String usernameType = usernameField.getType().getName();
            System.out.println("usernameType="+usernameType);
            //对username设值//类似于对象名demo.username="小明";
            usernameField.set(obj,"小明");
            //获取username的值->类似于对象名String username=demo.username;
            Object username = usernameField.get(obj);
            System.out.println("username="+username);

        } catch (Exception e) {
            e.printStackTrace();
        }
        //输出结果
        //usernameType=java.lang.String
        //username=小明
    }
}

例子1通过放射改造工厂设计模式

先上普通的工厂设计模式的代码

interface Message {
    public void send(String message);
}
//实现方法一
class  NewsPaper implements Message {
    @Override
    public void send(String message) {
        System.out.println("新闻报道:"+message);
    }
}
//实现方法二
class  NewsBook implements Message {
    @Override
    public void send(String message) {
        System.out.println("书籍报道:"+message);
    }
}
class MyFactory{
    private MyFactory(){}
    public static Message MessageObj(String classname){
        Message message = null;
        if("NewsPaper".equalsIgnoreCase(classname)){
            message = new NewsPaper();
        }else if("NewsBook".equalsIgnoreCase(classname)){
            message = new NewsBook();
        }
        return message;
    }
}
public class Main {
    public static void main(String[] args) {
        //使用NewsPaper实现Message
        Message NewsPaper = MyFactory.MessageObj("NewsPaper");
        NewsPaper.send("今日关注");
        //使用NewsBook实现Message
        Message NewsBook = MyFactory.MessageObj("NewsBook");
        NewsBook.send("红楼梦");
        //结果
        //新闻报道:今日关注
        //书籍报道:红楼梦
    }
}

如果实现接口的方法越来越多。在不断变化的话。工厂类就得改来改去。如何不增加实现接口的类而不动工厂类呢?

反射方式的工厂设计模式

interface Message {
    public void send(String message);
}
//实现方法一
class  NewsPaper implements Message {
    @Override
    public void send(String message) {
        System.out.println("新闻报道:"+message);
    }
}
//实现方法二
class  NewsBook implements Message {
    @Override
    public void send(String message) {
        System.out.println("书籍报道:"+message);
    }
}
class MyFactory{
    private MyFactory(){}
    public static Message MessageObj(String classname){
        Message message = null;
        try {
            message= (Message) Class.forName(classname).getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return message;
    }
}
public class Main {
    public static void main(String[] args) {
        //使用NewsPaper实现Message
        Message NewsPaper = MyFactory.MessageObj("com.company.NewsPaper");
        NewsPaper.send("今日关注");
        //使用NewsBook实现Message
        Message NewsBook = MyFactory.MessageObj("com.company.NewsBook");
        NewsBook.send("红楼梦");
        //结果
        //新闻报道:今日关注
        //书籍报道:红楼梦
    }
}

可以看到不管实现类怎么增加都无需动工厂类,这就是动态编译的好处

例子2-放射的自动赋值

需求:把有一个map的值赋值到user的对象中。
平常方法:可以一个个设置如:user.setKey(map.get(“key”));
问题是如果user中有上百个上千个值呢?一个个设那不是头发都没了吗?直接上代码

class  User{
    private String username;
    private String password;
    private String sex;
    private String roleName;
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }
    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", sex='" + sex + '\'' +
                ", roleName='" + roleName + '\'' +
                '}';
    }
}
//把字符串的第一个字符变大写
class  StringUitl{
    public static String toUpperCase(String arg){
        if (arg.length()==0){
            return null;
        }else if (arg.length()==1){
            return arg.toUpperCase();
        }else{
            return arg.substring(0,1).toUpperCase()+arg.substring(1);
        }
    }
}
class SetObjectByMap{
    private SetObjectByMap(){};
    public static Object create(Class<?> clazz,Map<String,String> map){
        Object obj=null;
        try {
            //获取类对象->本例子的User
             obj = clazz.getDeclaredConstructor().newInstance();
             //迭代map的值
            Iterator<Map.Entry<String, String>> iter = map.entrySet().iterator();
             while (iter.hasNext()){
                 Map.Entry<String, String> en = iter.next();
                 //StringUitl.toUpperCase(en.getKey());头字母变大写
                 //en.getKey();是成员的名字如username;set加上usernam第一个字母变大写得methodName=setUsername
                //假设en.getKey()=username则在里要执行的是SetUsername这个方法
                 String methodName = "set" + StringUitl.toUpperCase(en.getKey());
                 Method setMethod = obj.getClass().getDeclaredMethod(methodName,String.class);
                 String paramValue = en.getValue();
                 //开始赋值
                 setMethod.invoke(obj,paramValue);
             }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return obj;
    }
}
public class Main {
    public static void main(String[] args) {
         Map<String,String> map = new HashMap<>();
         map.put("username","小明");
         map.put("password","123");
         map.put("sex","女");
         map.put("roleName","管理员");
        User user = (User) SetObjectByMap.create(User.class, map);
        System.out.println(user);
        //结果
        //User{username='小明', password='123', sex='女', roleName='管理员'}
    }
}

这就类似于springMVC参数的自动赋值。当然这个只是简单的一级还有多级,和多种类型成员的赋值。读者可以举一反三。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值