理解java中反射,区别Class.forName(),Class.forName().instance() ,new,如果获取对象中的方法和字段

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/csdnliuxin123524/article/details/81951459

先了解一些基本的概念:运行时,编译时,编译型,解释型,类加载器,动态加载类 
什么是编译?将原程序翻译成计算机语言,就是二进制代码,在java中是将.java文件也就是源程序翻译成.class的字节码 
什么是编译时?将原程序翻译成计算机语言的过程中,将.java翻译为.class文件的过程 
什么是运行时?就是在启动这个程序的时候,在java中是,类加载器加载.class文件,并交给jvm处理 
什么是编译型语言?将原程序一次性全部转换为二进制代码,然后执行程序 
什么是解释型语言?转换一句,执行一句,java是既编译又解释的语言 
编译型语言和解释型语言的区别:编译型语言效率高,依赖于编译器,但是跨平台差,解释型的效率低,依赖于解释器,但跨平台强 
什么是类加载器?类加载器就是JVM中的类装载器,作用就是将编译好的.class字节码运到检查器进行安全检查的,检查通过后开始解释执行 
什么是运行时动态加载类? 
反射就是可以将一个程序(类)在运行的时候获得该程序(类)的信息的机制,也就是获得在编译期不可能获得的类的信息,因为这些信息是保存在Class对象中的,而这个Class对象是在程序运行时动态加载的 
它 就是可以在程序运行的时候动态装载类,查看类的信息,生成对象,或操作生成对象。类在运行的时候,可以得到该类的信息,并且 可以动态的修改这些信息。class对象是在运行的时候产生的,通过class对象操作类的信息是在运行时进行的,当运行 程序的时候,类加载器会加载真正需要的类,什么是真正需要的呢?就是该类真正起作用,如:有该类的对象实例,或该类调用了静态方法属性等 

那么如何实现反射呢?

要正确使用Java反射机制就得使用java.lang.Class这个类。它是Java反射机制的起源。当一个类被加载以后,Java虚拟机就会自动产 生一个Class对象。通过这个Class对象我们就能获得加载到虚拟机当中这个Class对象对应的方法、成员以及构造方法的声明和定义等信息。 三种方式得到Class对象: 

1.调用对象的getClass方法,返回该对象的Class对象。

2.Class.forName(“类的完整名字”);可以在类不确定的情况下实例化Class,最灵活。

3.Class c=类名.class 

举例说明吧:

package test;
/**
 * 
 * @author liuxin
 * @date   2018年8月22日
 */
public class A {
	
	private String name="ees";
	
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public A (){
		System.out.println("默认构造方法执行了");
	}
	
	static{
		System.out.println("执行静态代码块");
	}
	
	{System.out.println("执行非静态代码块");}
	

}

测试类:

package test;
/**
 * 
 * @author liuxin
 * @date   2018年8月22日
 */
public class Test {

	public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
		// TODO Auto-generated method stub
		A a=new A();
		System.out.println(a.getClass());
		System.out.println(Class.forName("test.A"));
		System.out.println(A.class);
	}
}

上面依次是三种得到class对象对象的方式:

执行结果:

执行静态代码块
执行非静态代码块
默认构造方法执行了
class test.A
class test.A
class test.A

这里要注意是先执行非静态代码块,再执行默认构造方法。

-----------------------------------------------------------------------------------------------------------------------------------------------------

下面我们回过头看class.forName()方法,这个方法提供了两个重载方法

先看第一个:

package test;
/**
 * 
 * @author liuxin
 * @date   2018年8月22日
 */
public class Test {

	public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
		// TODO Auto-generated method stub
//		A a=new A();
//		System.out.println(a.getClass());
//		System.out.println(Class.forName("test.A"));
//		System.out.println(A.class);
		
//		Class c=Class.forName("test.A");
		
		
		Class c=Class.forName("test.A",false,A.class.getClassLoader());
	}
}

true:是否实例化该类,也就是说实际上调用Class.forName(“类的完整名字”)加载类时执行初始化

this.getClass().getClassLoader()调用类加载器

上面forName第二个参数是false,因此,不会实例化,因此上面没有任何出入结果,这个例子证实了先执行类加载器,再执行静态方法块。

把false改成true,就会有输出结果:

执行静态代码块

再看第二个forname重载方法:

package test;
/**
 * 
 * @author liuxin
 * @date   2018年8月22日
 */
public class Test {

	public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
		// TODO Auto-generated method stub
//		A a=new A();
//		System.out.println(a.getClass());
//		System.out.println(Class.forName("test.A"));
//		System.out.println(A.class);
		
//		Class c=Class.forName("test.A",true,A.class.getClassLoader());
		
		Class c=Class.forName("test.A");
	}
}

这个结果就跟上面一样了,

执行静态代码块

可是上面的实例化的对象是个class,并没有准确到一个确切对象,那么怎么使用forName来创建呢?

package test;
/**
 * 
 * @author liuxin
 * @date   2018年8月22日
 */
public class Test {

	public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
		// TODO Auto-generated method stub
//		A a=new A();
//		System.out.println(a.getClass());
//		System.out.println(Class.forName("test.A"));
//		System.out.println(A.class);
		
//		Class c=Class.forName("test.A",true,A.class.getClassLoader());
		
//		Class c=Class.forName("test.A");
		A a= (A) Class.forName("test.A").newInstance();
		System.out.println(a.getName());
	}
}

结果:

执行静态代码块
执行非静态代码块
默认构造方法执行了
ees

需要补充的是:Class.forName("完整类名").newInstance();的用法相当于直接new();

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------

这里证实一个类再类加载中只加载一次:

package test;
/**
 * 
 * @author liuxin
 * @date   2018年8月22日
 */
public class Test {

	public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
		// TODO Auto-generated method stub
		A a1=new A();
		A a2=new A();
//		System.out.println(a.getClass());
//		System.out.println(Class.forName("test.A"));
//		System.out.println(A.class);
		
//		Class c=Class.forName("test.A",true,A.class.getClassLoader());
		
//		Class c=Class.forName("test.A");
		A a= (A) Class.forName("test.A").newInstance();
		
		System.out.println(a.getName());
	}
}

结果:

执行静态代码块
执行非静态代码块
默认构造方法执行了
执行非静态代码块
默认构造方法执行了
执行非静态代码块
默认构造方法执行了
ees

这里相当于new了3次 ,但是只执行了一次静态代码块。

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

下面通过反射获取类中的方法、字段等属性:

package test;

import java.lang.reflect.Method;

import org.apache.tools.ant.types.CommandlineJava.SysProperties;

/**
 * 
 * @author liuxin
 * @date   2018年8月22日
 */
public class Test {

	public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
		// TODO Auto-generated method stub
//		A a1=new A();
//		System.out.println(a.getClass());
//		System.out.println(Class.forName("test.A"));
//		System.out.println(A.class);
//		Class c=Class.forName("test.A");
//		A a= (A) Class.forName("test.A").newInstance();
		
		
		Class c=Class.forName("test.A",true,A.class.getClassLoader());
		Method[] methods=c.getMethods();
		for(Method m:methods){
			System.out.println(m);
		}
	}
}

结果:

执行静态代码块
public java.lang.String test.A.getName()
public void test.A.setName(java.lang.String)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()

同理,还有获取所有字段的方法:

Field[] fields=c.getDeclaredFields();
		for(Field f:fields){
			System.out.println(f);
		}

猜你喜欢

转载自blog.csdn.net/csdnliuxin123524/article/details/81951459