一、反射
一)基本概念
1.使用的包:
java.lang.reflect
2. 定义:
3.Object类中定义了getClass方法,返回一个类型为Class的对象。例:
法1:Class clazz = Student.class;//将student类抽象出一个类对象
法2:Class clazz = 对象.getClass();
二)通过反射可以返回的主要信息:
- getName() ;//获得该类的名称
- getMethods();//以数组形式获得所有权限为public的方法
- getMethod(String name,–);//获得权限为public的指定方法
- getDeclaredMethods();//以数组形式获得所有方法,按声明顺序返回
getDeclaredMethod(String name);//获得指定方法
getFields();//以数组形式获得所有权限为public的成员变量
- getField(String name,–);//获得指定成员变量
- getDeclaredFields();//以数组形式获得所有成员变量,按声明顺序返回
- getDeclaredField(String name);//获得指定成员变量
- getClasses();//获得权限为public的内部类
- getDeclaredClasses();//获得所有内部类
三)范例1:
/*要求:
1.利用反射获取一个类中的成员属性,并输出属性名称,以及类型。
2.实现一种情况的解决办法:
当类中的某一个封装好的属性,没有set方法,通过反射的方法,实现对此属性的赋值。
*/
/*Student 类,利用反射对其中的属性age赋值*/
public class Student {
private int age;
public String field1;
public int field2;
public int getage(){
return age;
}
}
import java.lang.reflect.Field;
public class TestFeild {
/*
* Class是个抽象所有类的类
* Feild有属性和名称。
* */
public static void main(String[] args) {
Class<Student> clazz = Student.class; //此clazz是Class对象
Field[] fields = clazz.getFields(); //以数组形式返回的是Student类中所有的公共属性
Field[] fields2 = clazz.getDeclaredFields();//以数组形式返回所有属性
for(Field field:fields2){
System.out.println(field.getName());//属性名
System.out.println(field.getType().getName());//属性类型的名称
System.out.println(field.getModifiers());//返回属性修饰符额整数表示形式
}
Student zhangsan = new Student();
//由于age没有set方法,这里通过下面方法来写入age值
//先取消java访问修饰符检查,改好值后,在设回false
try {
Field age = clazz.getDeclaredField("age");//得到指定名称的属性age
age.setAccessible(true);//取消java访问修饰符检查(这里取消private的访问)
age.set(zhangsan, 19); //设值,(IllegalArgumentException异常)
age.setAccessible(false);//取消原来的检查
System.out.println(zhangsan.getage());
} catch (NoSuchFieldException | SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
结果演示:
四)范例2:
/*
* 功能:利用反射来构造函数
* 步骤:创建一个工厂:
* 1.在里面放入一个造人方法。
* 2.建对象Properties对象
* 3.加载config文件
* 4.获得属性getProperty()方法
* 5.获得类的对象forName()
* 通过newInstance方法创建这个类的一个对象。
*
* */
//工厂类
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
public class Factory {
public static Person creatPerson(){//创建一个造人的方法
Person person = null;
Properties p = new Properties(); //建对象Properties
try {
p.load(new FileInputStream("config.properties"));//
String clazzName = p.getProperty("person");//获得person
Class clazz = Class.forName(clazzName);
person = (Person) clazz.newInstance(); //通过反射来构造函数
} catch (ClassNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return person;
}
}
//Person 类。
public class Person {
public void run(){
System.out.println("人在跑步!");
}
}
//Student 类
public class Student extends Person{
public void run(){
System.out.println("学生在跑跑!");
}
}
//Teacher 类
public class Teacher extends Person{
public void run(){
System.out.println("老师在跑步!");
}
}
//所添加的普通文件
person = com.day0804_1.Student
//测试类
/*优点:通过使用Properties,建立config文件,将使用的类在此定义,方便以后的扩展,因为它不需要编译。*/
public class TestFactory {
public static void main(String[] args) {
// 反射,工厂设计模式
Person person = Factory.creatPerson();
person.run();
}
}
演示:
二、注解 Annotation类(这里介绍自定义注解)
一)定义:
该功能建立在反射的基础上,
1.在定义Annotation类时,通过@Target来设置Annotation类型适用的程序元素类型,参数为枚举类型ElementType中的枚举常量:
这里介绍的是:ElementType.FIELD //表示成员变量和枚举常量,
其他常量通过API查找。
2.在定义Annotation类时,通过@Retention来设置Annotation类型的有效范围,参数类型为枚举类型RetentionPolicy中的枚举常量:
1)RetentionPolicy.SOURCE :表示不编译Annotation到类文件中,范围最小
2)RetentionPolicy.RUNTIME:表示编译到类文件,但运行时不加载到jvm中。
3)RetentionPolicy.RUNTIME:表示运行时将Annotation加载到jvm中,有效范围最大
3.int age() default 19;//设默认值
使用时用自定义注释名称(name=–)
二)范例1:
/*功能:1.自定义注释;2.利用注释注解某个类中的属性,并使此类初始化时就将注解传入的值传入该属性。
*/
import java.lang.annotation.ElementType;
/*
* 功能:自定义注解
* */
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) //@Retention可以设置Anotation的范围,三种参数,RUNTIME范围最大
@Target(ElementType.FIELD) //表示此注解只能用于成员变量和枚举常量
public @interface StudentAnotation {
int value();//表示此注解可以注解第一个int
}
//使用注释的类
import java.lang.reflect.Field;
public class Student {
@StudentAnotation(18) //测试了可以来注解student中的变量,注:是变量,不能注释方法
private int age;
@StudentAnotation(7)
private int j ;
public Student(){
Class clazz = Student.class;//得到student类的Class类或写成:Class clazz = this.getClass();
try {
Field field = clazz.getDeclaredField("age");//得到age属性
StudentAnotation sa = field.getAnnotation(StudentAnotation.class);
if(sa!=null){
int i = sa.value();//得到属性默认值
field.setAccessible(true);
field.set(this, i);//this指的是student对象,将i值赋给了这个对象。
field.setAccessible(false);
}
} catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}//得到age值
catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public int getAge() {
return age;
}
}
//测试
public class TestAnnoration {
public static void main(String[] args) {
Student zhangsan = new Student();
System.out.println(zhangsan.getAge());
}
}
/*
结果:
19
*/
三、范例2:
/*要求:范例1的升级版,利用反射传入的值传入一个类的引用型对象中。
*/
/*自定义一个注解类:
* 控制它的注解范围和注解位置
* 定义几个注解值注:此处定义的name和age名字都不是唯一的,如果写成value(),在用时直接用: 自定义类名(值)即可。
* 注意,注解的格式@----()后面没有任何符号
* */
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface TeacherAnnotation {
String name();
int age() default 19;//如果age没有设定值就默认为19
}
//clazz中需要用的引用类型
public class Teacher {
private String name;
private int 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;
}
}
/*Clazz类中:使用注释的类
* 1.此类中定义了很多成员变量,其中包括几个Teacher变量,并加了注解
* 2.为使Clazz对象建立时就赋值,将代码写到构造器中。
* 3.获得Clazz类的Class类对象;获得所有属性;判断哪些属性有注解,并获得注解
* 4.由于注解的成员变量是一个引用类,这里建立teacher对象,将获得的注解值传入,
* 5.最后将teacher对象赋给该clazz。由于是该属性去调用set方法,参数是(该clazz对象,teacher对象)
* */
import java.lang.reflect.Field;
public class Clazz {
@TeacherAnnotation(age = 20,name = "张三")
private Teacher javaTeacher;
@TeacherAnnotation(age = 23,name = "王五")
private Teacher englishTeacher;
@TeacherAnnotation(age = 10,name = "李四")
private Teacher mathTeacher;
private int clazzTeaher;
private String name;
//为使对象初始化就赋值进注解,则将代码写到构造器中
public Clazz(){
Class<Clazz> clazz = Clazz.class;
Field[] fields = clazz.getDeclaredFields();//getDeclaredFields是获得总属性数组
for(Field field:fields){
//得到TeacherAnnotation注解,如果没有注解就返回null
TeacherAnnotation ta = field.getAnnotation(TeacherAnnotation.class);
if(ta==null){
continue;
}else{
int age = ta.age();
String name = ta.name();
//建立teacher对象
Teacher teacher = new Teacher();
teacher.setAge(age);
teacher.setName(name);//根据注解的值创建对象
field.setAccessible(true);
try {
field.set(this, teacher);//将这个teacher对象赋值给此对象的该属性
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
field.setAccessible(false);
}
}
}
public Teacher getJavaTeacher() {
return javaTeacher;
}
public void setJavaTeacher(Teacher javaTeacher) {
this.javaTeacher = javaTeacher;
}
public Teacher getEnglishTeacher() {
return englishTeacher;
}
public void setEnglishTeacher(Teacher englishTeacher) {
this.englishTeacher = englishTeacher;
}
public Teacher getMathTeacher() {
return mathTeacher;
}
public void setMathTeacher(Teacher mathTeacher) {
this.mathTeacher = mathTeacher;
}
}
//测试:
/*
* 功能:利用反射方法,获取到注解的值。
* */
public class Test {
public static void main(String[] args) {
Clazz clazz = new Clazz();
System.out.println(clazz.getEnglishTeacher().getName());
System.out.println(clazz.getJavaTeacher().getAge());
}
}
注:
范例一和范例二的区别:
在于前者直接获取到注解,直接传给该对象中的该属性。
后者获得属性值传到它的对象,将这个对象传到该对象的这个类属性。