我们先来看看反射能干什么,不要求理解
一、反射的作用
这是我们要操作的Person类
public class Person {
private String name;
public int age;
public Person() {
}
private Person(String name) {
this.name = name;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public void show(){
System.out.println("我是一个人");
}
private String showNation(String nation){
System.out.println("我的国籍是:"+nation);
return nation;
}
}
然后我们可以通过反射可以去创建对象,调取对象的指定属性和方法,还可以调私有的,这是以前不能实现的,具体看下代码
你可以测试一下,感受一下
import org.junit.Test;
import java.lang.annotation.ElementType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectTest {
@Test
public void test1(){
//没有反射之前我们是不能去使用类的私有结构
Person p1=new Person();
p1.age=10;
System.out.println(p1);
p1.show();
}
@Test
public void test2() throws Exception{
Class clazz=Person.class;
//1、通过反射创建一个类的对象
Constructor cons=clazz.getConstructor(String.class,int.class);
Object obj=cons.newInstance("Tom",12);
Person p2= (Person) obj;
System.out.println(p2);
//2、通过反射,调取对象指定属性和方法
//调用属性
Field age= clazz.getDeclaredField("age");
age.set(p2,10);
System.out.println(p2);
//调用方法
Method show=clazz.getDeclaredMethod("show");
show.invoke(p2);
System.out.println("========================================");
//3、通过反射调用私有的构造器和方法。属性
//调用私有构造器
Constructor cons2=clazz.getDeclaredConstructor(String.class);
cons2.setAccessible(true);
Person p1= (Person) cons2.newInstance("Merry");
System.out.println(p1);
//调用私有属性
Field name=clazz.getDeclaredField("name");
name.setAccessible(true);
name.set(p1,"Hameimei");
System.out.println(p1);
//调用私有的方法
Method showNation=clazz.getDeclaredMethod("showNation",String.class);
showNation.setAccessible(true);
String nation= (String) showNation.invoke(p1,"中国");//相当于String nation=p1.showNation("中国");
System.out.println(nation);
}
看完之后,你肯定然后会有两个疑问
1、什么时候用反射?
不确定要调用那个类,就是需要动态性的时候使用反射
2、怎么看反射可以调用私有的结构,是否与封装性矛盾?
不矛盾,反射能不能调的问题,封装是建议你去不去调的问题
二、关于java.Lang.Class类的理解
1、类的加载过程:
程序经过javac.exe命令后,会生成一个或多个字节码文件(.class结尾)。
接着我们用java.exe命令对某个字节码文件进行解释运行,相当于把某个字节码
文件加载到内存中,此过程称为类的加载,加载到内存中的类,我们就称为运行时类,
此运行时类,就作为Class的一个实例。也可以理解为,运行时类是Class的一个对象,
也就是万事万物皆对象。无极之外复无极也。
2、加载到内存中的运行时类,会缓存一段时间,在此时间之内,我们可以通过不同的方式来
获取此运行时类。
*/
//获取Class类的实例的方式
@Test
public void test3()throws Exception{
//方式一:调用运行时类的属性
Class clazz1=Person.class;
System.out.println(clazz1);
//方式二:通过调用运行时类的对象,调用getClass()
Person p=new Person();
Class clazz2=p.getClass();
System.out.println(clazz2);
//方式三:通过调用Class类的静态方法:forname(String classpath)
Class clazz3=Class.forName("com.guo.java.Person");
System.out.println(clazz3);
System.out.println(clazz1==clazz2);
//方式四:使用类的加载器:ClassLoader
ClassLoader classLoader=ReflectTest.class.getClassLoader();
Class clazz4=classLoader.loadClass("com.guo.java.Person");
System.out.println(clazz4);
}
//Class实例可以是哪些结构的说明:
/*
1)、class(外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类)
2)、interface(接口)
3)、[](数组)
4)、enum(枚举)
5)、annotation(注解@interface)
6)、primitive type(基本数据类型)
7)void
*/
@Test
public void test4(){
Class c1=Object.class;
Class c2=Comparable.class;
Class c3=String[].class;
Class c4=int[][].class;
Class c5= ElementType.class;
Class c6=Override.class;
Class c7=int.class;
Class c8=void.class;
Class c9=Class.class;
int[] a=new int[10];
int[] b=new int[100];
Class c10=a.getClass();
Class c11=b.getClass();
//只要数组类型和维度一样,就是同一个Class。
System.out.println(c10==c11);
}
}
下面是一个体会反射动态性的一个例子,随机创建一个类的对象,test1()是创建对应运行时类的对象,test2()是通过getInstance()方法来随机创建一个运行时的对象,就不像以前那样需要传入一个具体的对象来创建了。
import org.junit.Test;
import java.util.Random;
/*
通过反射创建对应运行时的类的对象
*/
public class NewInstanceTest {
@Test
public void test1()throws Exception{
Class<Person> clazz=Person.class;
//调用newInstance()方法创建对应运行时的类的对象,内部是调用了运行时类的空参构造器
/*
下方法正常创建对象的要求是:
1、运行是类有空参的构造器
2、要求空参的构造器必须访问权限得够,通常设置为public
在javabean中要求提供一个空参的构造器,原因:
1,便于通过反射创建运行时类的对象,
2,便于子类继承父类时,默认调用super时,保证父类有此构造器
*/
Person obj=clazz.newInstance();
System.out.println(obj);
}
//体会反射的动态性
@Test
public void test2(){
int num=new Random().nextInt(3);//0,1,2
String classpath="";
switch (num){
case 0:
classpath="java.util.Date";
break;
case 1:
classpath="java.lang.Object";
break;
case 2:
classpath="com.guo.java.Person";
break;
}
try {
Object obj=getInstance(classpath);
System.out.println(obj);
} catch (Exception e) {
e.printStackTrace();
}
}
public Object getInstance(String classpath)throws Exception{
Class clazz= Class.forName(classpath);
return clazz.newInstance();
}
}
三、类加载器的使用
类加载器的作用:
将class文件字节码内容加载到内存中,并将这些静态数据转化成方法区的运行时的数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。
类缓存:
标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间,不过JVM垃圾回收这些Class对象。
类加载器作用是用来把类(class)装载进内存的,JVM规范定义了如下类型的类加载器
引导类加载器:用C++编写的,是JVM自带的类加载器,负责Java平台核心库,用来装载核心类库,无法被直接获取。
扩展类加载器:负责jre/lib/ext目录下的jar包或…D java.ext.dirs指定目录下的jar包装入工作库。
系统类加载器:负责java—classpath或…D java class.path所指的目录下的类与jar包装入工作,是常用的类加载器。
下面我们来看类加载器代码的具体使用,还有一个Properties配置文件的使用
import org.junit.Test;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Properties;
//类加载器的使用
public class ClassLoaderTest {
@Test
public void test1(){
//对于自定义的类,使用系统类加载器进行加载
ClassLoader classLoader=ClassLoaderTest.class.getClassLoader();
System.out.println(classLoader);
//调用系统类加载器的getParent():获取扩展类加载器
ClassLoader classLoader1=classLoader.getParent();
System.out.println(classLoader1);
//调用扩展类加载器的getParent():无法获取引导类加载器
//引导类的加载器主要负责加载Java的核心类库,无法加载自定义类
ClassLoader classLoader2=classLoader1.getParent();
System.out.println(classLoader2);
ClassLoader classLoader3=String.class.getClassLoader();
System.out.println(classLoader3);
}
/*
Properties:用来读取配置文件
*/
@Test
public void test2 ()throws Exception{
Properties pros=new Properties();
//此时文件默认在module下
//读取配置文件方式一:
// FileInputStream fis=new FileInputStream("jdbc.properties");
// pros.load(fis);
//读取配置文件方式二:
//配置文件默认识别在module的src下
ClassLoader classLoader=ClassLoaderTest.class.getClassLoader();
InputStream is=classLoader.getResourceAsStream("jdbc.properties");
pros.load(is);
String user=pros.getProperty("user");
String password=pros.getProperty("password");
System.out.println(user+" "+password);
}
}
四、通过反射获取运行时类的完整结构
还是来一个Person的操作类,这个类创建的比较全面,便于反射的操作能面面俱到。
父类:
import java.io.Serializable;
public class Creature<T> implements Serializable {
private char gender;
public double weight;
private void breath(){
System.out.println("生物呼吸");
}
public void eat(){
System.out.println("生物吃东西");
}
}
接口:
public interface Myinterface {
void info();
}
注解:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "hello";
}
子类
public class Person extends Creature<String> implements Comparable<String>,Myinterface{
private String name;
int age;
public int id;
public Person() {
}
@MyAnnotation(value = "abc")
private Person(String name) {
this.name = name;
}
Person(String name, int age) {
this.name = name;
this.age = age;
}
@MyAnnotation
private String show(String nation){
System.out.println("我的国籍是:"+nation);
return nation;
}
public String display(String insterests){
return insterests;
}
@Override
public void info() {
System.out.println("我是一个人");
}
@Override
public int compareTo(String o) {
return 0;
}
private static void showDesc(){
System.out.println("我是一个可爱的人");
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", id=" + id +
'}';
}
}
获取当前运行时类的属性结构
import com.guo.java1.Person;
import org.junit.Test;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
/*
获取当前运行时类的属性结构
*/
public class FieldTest {
@Test
public void test1(){
Class clazz = Person.class;
//获取属性结构
//getFields():获取运行时类和父类声明为public访问权限的属性
Field[] fields=clazz.getFields();
for (Field f:fields){
System.out.println(f);
}
System.out.println();
//getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性)
Field[] df=clazz.getDeclaredFields();
for (Field f:df){
System.out.println(f);
}
}
//权限修饰符,数据类型,变量名
@Test
public void test2(){
Class clazz = Person.class;
Field[] df=clazz.getDeclaredFields();
for (Field f:df){
//1、权限修饰符
int modifiers=f.getModifiers();
System.out.print(Modifier.toString(modifiers)+"\t");
//2、数据类型
Class type=f.getType();
System.out.print(type.getName()+"\t");
//3、变量名
String fname=f.getName();
System.out.println(fname);
}
}
}
获取运行时类的方法结构
import com.guo.java1.Person;
import org.junit.Test;
import java.lang.reflect.Method;
//获取运行时类的方法结构
public class MethodTest {
@Test
public void test1(){
Class clazz= Person.class;
//getMethods():获取运行时类和父类声明为public访问权限的方法
Method[] methods=clazz.getMethods();
for (Method m:methods){
System.out.println(m);
}
System.out.println();
//getDeclaredMethods():获取当前运行时类中声明的所有方法。(不包含父类中声明的方法)
Method[] methods1=clazz.getDeclaredMethods();
for (Method m:methods1){
System.out.println(m);
}
}
//可以获取注解,权限修饰符,返回值类型,方法名(参数)throes xxxException{},与属性一样的调用相应方法
//就行,查资料就可以了。。。。
}
获取其他的一些构造
import com.guo.java1.Person;
import com.sun.xml.internal.bind.v2.schemagen.xmlschema.Annotated;
import org.junit.Test;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
public class OtherTest {
//获取构造器
@Test
public void test1(){
Class clazz= Person.class;
//getConstructors():获取当前运行时类中声明为public的构造器
Constructor[] constructor=clazz.getConstructors();
for (Constructor c:constructor){
System.out.println(c);
}
System.out.println();
//getDeclaredConstructors():获取当前运行时类中声明的所有构造器
Constructor[] constructors=clazz.getDeclaredConstructors();
for (Constructor c:constructors){
System.out.println(c);
}
}
//获取运行时类的父类
@Test
public void test2(){
Class clazz=Person.class;
Class superclass=clazz.getSuperclass();
System.out.println(superclass);
}
//获取运行时类的带泛型的父类的泛型
@Test
public void test3(){
Class clazz=Person.class;
Type genericSuperclass=clazz.getGenericSuperclass();
ParameterizedType parameType=(ParameterizedType)genericSuperclass;
//获取泛型类型
Type[] actualType=parameType.getActualTypeArguments();
// System.out.println(actualType[0].getTypeName());
System.out.println(((Class)actualType[0]).getName());
}
//获取运行时类实现的接口
@Test
public void test4(){
Class clazz=Person.class;
Class[] interfaces = clazz.getInterfaces();
for (Class c:interfaces){
System.out.println(c);
}
System.out.println();
//获取运行时类父类实现的接口
Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
for (Class c:interfaces1){
System.out.println(c);
}
}
//获取运行时类所在的包
@Test
public void test5(){
Class clazz=Person.class;
Package Package = clazz.getPackage();
System.out.println(Package);
}
//获取运行时类的注解
@Test
public void test6(){
Class clazz=Person.class;
Annotation[] annotations = clazz.getAnnotations();
for (Annotation a:annotations){
System.out.println(a);
}
}
}
五、通过反射来调用运行时类的指定结构(重点)
具体看代码,都是一些实现的方法,在后面的框架学习中,都是用反射,注解,设计模式的组合来实现的,因为框架的复用性,所以在类不确定的情况下,我们可以运用反射机制来实现你运行中产生类的具体属性,方法操作。
import com.guo.java1.Person;
import org.junit.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
//调用运行时类的指定结构:属性,方法,构造器
public class ReflectionTest {
//获取指定属性方式一(不理想,不常用)
@Test
public void testField()throws Exception{
Class clazz= Person.class;
//创建运行时类的对象
Person p = (Person) clazz.newInstance();
//获取指定的属性:要求运行时类中的属性为public
Field id = clazz.getField("id");
//设置指定属性的值
//set():参数1:指明设置哪个对象的属性 参数2:将此属性值设置为多少
id.set(p,1001);
//获取当前属性的值
//get():参数1:获取哪个对象的当前属性值
int pid=(int)id.get(p);
System.out.println(pid);
}
//获取指定属性方式二(开发中都是使用这种)
@Test
public void testField1()throws Exception{
Class clazz= Person.class;
//创建运行时类的对象
Person p = (Person) clazz.newInstance();
//获取指定的属性:不需要运行时类中的属性为public
Field name = clazz.getDeclaredField("name");
//提高name的权限,保证当前属性可以被访问
name.setAccessible(true);
//设置指定属性的值
//set():参数1:指明设置哪个对象的属性 参数2:将此属性值设置为多少
name.set(p,"tom");
//获取当前属性的值
//get():参数1:获取哪个对象的当前属性值
String pid=(String)name.get(p);
System.out.println(pid);
}
//如何操作运行时类的指定方法(重点)
@Test
public void testMethod()throws Exception{
Class clazz= Person.class;
//创建运行时类的对象
Person p = (Person) clazz.newInstance();
//1、获取指定的某个方法
//getDeclaredMethod():参数1:指明方法的名称 参数2:指明获取的方法的形参列表
Method show = clazz.getDeclaredMethod("show",String.class);
//2、保证此方法是可访问的
show.setAccessible(true);
//3、invoke():参数1:方法的调用者 参数2:给方法形参赋值的实参
//invoke()返回值即为对应类中调用的方法的返回值。
Object obj=show.invoke(p,"CHN");
System.out.println(obj);
System.out.println("===============如何调用静态方法===================");
//private static void showDesc(){}
Method showDesc = clazz.getDeclaredMethod("showDesc");
//如果调用的类没有返回值,则返回Null
showDesc.setAccessible(true);
Object o=showDesc.invoke(Person.class);
System.out.println(o);
}
//调用指定类的构造器
@Test
public void testConstruct()throws Exception{
Class clazz= Person.class;
//private Person(String name)
//1、getDeclaredConstructor():参数1:指明构造器的参数列表
Constructor declaredConstructor = clazz.getDeclaredConstructor(String.class);
//2、保证此构造器是可访问的
declaredConstructor.setAccessible(true);
//3、调用此构造器创建运行时类的对象
Person p=(Person) declaredConstructor.newInstance("tom");
System.out.println(p);
}
}