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参数的自动赋值。当然这个只是简单的一级还有多级,和多种类型成员的赋值。读者可以举一反三。