JAVA反射--一些常用方法


       反射中,最基础的是对Class类的了解和使用。在JAVA中Object是一切类的父类,而getClass()方法是Object中定义的,如下

public final native Class<?> getClass();

       那么可以这么说,所有类的对象实际上都是Class类的实例。如果你对类加载及JVM方法区有所了解,这个应该很容易理解。

       本文主要是写点代码认识Class类的一些常用方法。

1.获取Class对象

      在Class类中,只定义了个私有的构造方法,这意味着,无法通过new Class()方式创建一个Class对象。

/*
     * Constructor. Only the Java Virtual Machine creates Class
     * objects.
     */
    private Class() {}


      虽然无法直接使用new Class()方式创建对象,但是Class类中提供了forName()方法,通过它仍然可以获得Class对象。

public static Class<?> forName(String className) 
                throws ClassNotFoundException {
        return forName0(className, true, ClassLoader.getCallerClassLoader());
    }




public static Class<?> forName(String name, boolean initialize,
				   ClassLoader loader)
        throws ClassNotFoundException


        除了使用forName()方法获得Class对象外,上面说过了Object是所有类的父类,而Object中有getClass()方法,所以通过"类名.getClass()"也可以获得Class对象,也可以通过“类名.class"

package test;

public class A1 {
	
}


/**
 * 获取Class对象
 */
public class A2 {
	
	public static void main(String[] args){
		Class<?> c1=null;
		Class<?> c2=null;
		Class<?> c3=null;
		try {
		 c1=Class.forName("test.A1");//通过forName()获取
		 c2=A1.class;//通过"类名.class"获取
		 A1 a1=new A1();
		 c3=a1.getClass();//通过"对象.getClass()"获取
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		System.out.println("类路径:"+c1.getName());
		System.out.println("类路径:"+c2.getName());
		System.out.println("类路径:"+c3.getName());
	}
}


运行结果

类路径:test.A1
类路径:test.A1
类路径:test.A1

       通过运行结果可知,3种实例化Class对象的方式是一样的,但是使用forName()是较为常用的一种(当然,如果使用Hibernate或spring等框架时,经常使用"类.class“方式传送一个JavaBean实体)。

     

   下面先看过设计上比较丑陋的例子。

package test;
/**
 * 水果类接口
 * 所有水果必须实现此接口
 *
 */
public interface Fruit {
	public void say();
}



package test;
/**
 * 苹果类,实现接口Fruit
 *
 */
public class Apple implements Fruit {
	@Override
	public void say() {
		System.out.println("hello,I'm apple!");
	}
}



/**
 * 香蕉类,实现接口Fruit
 *
 */
public class Banana implements Fruit {

	@Override
	public void say() {
		System.out.println("hello,I'm banana!");
	}
}




package test;
/**
 * 水果工具类
 *
 */
public class FruitUtil {

	public static Fruit createFruit(String fruitName){
		if("apple".equalsIgnoreCase(fruitName)) return (Fruit)new Apple();
		if("banana".equalsIgnoreCase(fruitName)) return (Fruit)new Banana();
		return null;
	}
}



package test;
/**
 * 测试类
 *
 */
public class Test {
	
	public static void main(String[] args){
		Fruit f=FruitUtil.createFruit("apple");
		if(f!=null) {
			f.say();
		}else{
			System.out.println("没有水果!");
		}
	}
}

运行结果

hello,I'm apple!

 

      代码没错,运行结果也没错,我之所以说它丑陋,是从代码扩展性方面考虑。比如,如果我要再添加一种水果呢?那么就必须修改FruitUtil.createFruit()方法中的代码了,增加if判断。那如果我要新曾几十种水果呢?那是不是要写几十个if判断。。。。。作为一个接触JAVA两年的菜鸟的我,都觉得代码设计不友好了,更别说修改原有代码对系统的危害了。那有没有一种方法,当新增水果时,不需要对原有代码做任何修改呢?有,这时,Class.forName()一声大哄,粉墨登场了。

package test;
/**
 * 水果类接口
 * 所有水果必须实现此接口
 *
 */
public interface Fruit {
	public void say();
}


package test;
/**
 * 苹果类,实现接口Fruit
 *
 */
public class Apple implements Fruit {
	@Override
	public void say() {
		System.out.println("hello,I'm apple!");
	}
}



package test;
/**
 * 香蕉类,实现接口Fruit
 *
 */
public class Banana implements Fruit {

	@Override
	public void say() {
		System.out.println("hello,I'm banana!");
	}
}


package test;
/**
 * 水果工具类
 *
 */
public class FruitUtil {

	public static Fruit createFruit(String classPath)throws Exception{
		Fruit fruit=null;
		try {
			fruit=(Fruit)Class.forName(classPath).newInstance();
		} catch (Exception e) {
			e.printStackTrace();
			throw new Exception("创建水果失败!");
		} 
		return fruit;
	}
}


package test;

import java.io.File;
import java.io.FileInputStream;
import java.util.Properties;

/**
 * 测试类
 *
 */
public class Test {

	private final static String FRUIT_CONF_PATH=System.getProperty("user.dir");
	public static void main(String[] args){
		Properties p=new Properties();
		try {
			//在eclipse下建立普通JAVA项目,src写的内容放在bin下了
			FileInputStream in=new FileInputStream(new File(FRUIT_CONF_PATH+File.separator+"bin"+File.separator+"fruit.properties"));
			p.load(in);
			String fruitClassPath=p.getProperty("apple");
			p.clear();
			in.close();
			Fruit f=FruitUtil.createFruit(fruitClassPath);
			f.say();
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("获取水果实例失败!");
		}
	}
}

fruit.properties配置文件


apple=test.Apple
banana=test.Banana


 运行结果

hello,I'm apple!

 

    这样改写后,以后有新增水果时,只要编写新增水果类,并再配置文件中配置新水果的类路径就OK了,原有的代码不需要修改。

这样的设计就具有很好的扩展性。

 

2.获取类中的成员变量

       getFields():获得类(包括父类)的public成员变量

       getDeclaredFields():获得类(不包括父类)的全部成员变量

   

 

package test;

public class A {

	public int n_A;
	private String s_A;
	protected double d_A;
}

package test;

public class A1 extends A {
	public int n_A1;
	private String s_A1;
	protected double d_A1;
	
}

package test;

import java.lang.reflect.Field;

public class A2 {
	public static void main(String[] args){
		Class<?> c1=null;
		try {
		 c1=Class.forName("test.A1");//通过forName()获取A1的Class对象
		 Field[] f1=c1.getFields();//A1(包括父类)中的public成员变量
		 for(Field f:f1)
			 System.out.println("A1(包括父类)类中public 成员变量:"+f.getName());
		 System.out.println("获取A1(不包括父类)类中的所有成员变量::::::::");
		 Field[] f2=c1.getDeclaredFields();//A1中的所有成员变量
		 for(Field f:f2)
			 System.out.println("A1(不包括父类)类中的成员变量:"+f.getName());
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}

}


运行结果

A1(包括父类)类中public 成员变量:n_A1
A1(包括父类)类中public 成员变量:n_A
获取A1(不包括父类)类中的所有成员变量::::::::
A1(不包括父类)类中的成员变量:n_A1
A1(不包括父类)类中的成员变量:s_A1
A1(不包括父类)类中的成员变量:d_A1

3.获取类中的方法

      getMethods():获取类(包括父类)中的public方法(不包括构造方法)

      getDeclaredMethods():获取本类(不包括父类)中的所有方法(不包括构造方法)

      getConstructors():获取本类(不包括父类)中的所有public构造方法

     考虑到篇幅问题,就不贴代码了。

4.实例化对象

package test;

public class A1  {
	private int n;
	
	public A1(){
		this.n=0;
	}
	public A1(int n){
		this.n=n;
	}
	
	public int getN() {
		return n;
	}
	public void setN(int n) {
		this.n = n;
	};
}


package test;

import java.lang.reflect.Constructor;

public class A2 {
	public static void main(String[] args){
		Class<?> c1=null;
		try {
		 c1=Class.forName("test.A1");//通过forName()获取A1的Class对象
		 A1 a1=(A1)c1.newInstance();//使用这种实例化方式,则A1中必须有无参构造方法
		 System.out.println("使用Class类中的newInstance()方法实例化,a1.n="+a1.getN());
		 Constructor<?>[] con=c1.getConstructors();//类中存在多个构造方法时,数组顺序和类中写构造方法的顺序一致
		 A1 a2=(A1)con[0].newInstance();//A1中也必须存在无参构造方法
		 System.out.println("使用Constructor类中的public T newInstance(Object ... initargs)方法实例化,a2.n="+a2.getN());
		 A1 a3=(A1)con[1].newInstance(10);//A1中可以不存在无参构造方法,或者无参构造方法用private修饰时
		 System.out.println("使用Constructor类中的public T newInstance(Object ... initargs)方法实例化,a3.n="+a3.getN());
		 
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}

}

运行结果

使用Class类中的newInstance()方法实例化,a1.n=0
使用Constructor类中的public T newInstance(Object ... initargs)方法实例化,a2.n=0
使用Constructor类中的public T newInstance(Object ... initargs)方法实例化,a3.n=10

5.通过反射调用类中的方法

package test;

public class A {

	public void sayHello(){
		System.out.println("hello,world");
	}
	public void sayHello(String name){
		System.out.println("hello,"+name);
	}
}

package test;

import java.lang.reflect.Method;

public class Test01 {

	public static void main(String[] args){
		Class<?> c=null;
		try {
		 c=Class.forName("test.A");//通过forName()获取A的Class对象
		 A a=(A)c.newInstance();
		Method m1=c.getMethod("sayHello");//获得无参数的syaHello方法
		m1.invoke(a);
		Method m2=c.getMethod("sayHello", String.class);//获得只含有String类型参数的sayHello方法
		m2.invoke(a, "everyOne");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

运行结果

hello,world
hello,everyOne


6.通过反射破坏类的封装性(给私有变量赋值并访问)

package test;

public class User {

	private String name;
	private int age;
	public String getName() {
		return name;
	}
	public int getAge() {
		return age;
	}
	
}


package test;

import java.lang.reflect.Field;
/**
 * 通过反射访问类的私有变量
 *
 */
public class Test01 {

	public static void main(String[] args){
		Class<?> c=null;
		try {
		 c=Class.forName("test.User");//通过forName()获取A的Class对象
		 User user=(User)c.newInstance();
		Field name=c.getDeclaredField("name");//获得name变量
		Field age=c.getDeclaredField("age");//获得age变量
		name.setAccessible(true);//将name属性设置成可被外部访问
		age.setAccessible(true);//将age变量设置成可被外部访问
		name.set(user, "张三");
		age.set(user, 20);
		System.out.println("姓名:"+name.get(user)+"   "+user.getName());
		System.out.println("年龄:"+age.get(user)+"   "+user.getAge());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

运行结果

姓名:张三   张三
年龄:20   20


 
 
 
 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值