反射
什么是反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的法的功能称为java语言的反射机制。
反射的作用
通过java语言中的反射机制可以操作字节码文件 (可以读和修改字节码文件)
通过反射机制可以操作代码片段。(class文件)
Class对象
简称:类对象
注意:一个类只有一个类对象,是在JVM加载该类时生成的
如何获取类对象:
方式1:使用类名获取
方式2:使用对象名获取
方式3:使用Class类提供的静态方法forName方法获取
方式 | 备注 |
---|
类名.class | 通过类名进行获取 |
对象名.getClass() | 使用对象进行获取 |
Class.forName(“完整路径,带包名”) | 通过Class的静态方法获取 |
类对象常用方法
类对象信息相关
getPackage():获取该类的包对象
getName():获取包名
getName():获取该类的类名,注意包名+类名
getSimpleName():只获取类名
getSuperclass():获取其父类的类对象
getInterfaces():获取该类的所有实现的接口的类对象
使用方法:
package com.fs.demo01;
import java.io.Serializable;
public class Demo01 implements Serializable{
public static void main(String[] args) {
Class cls=Demo01.class;
Package pac=cls.getPackage();
String pacName=pac.getName();
System.out.println(pacName);
String enClsName=cls.getName();
System.out.println(enClsName);
String clsName=cls.getSimpleName();
System.out.println(clsName);
Class superName=cls.getSuperclass();
System.out.println(superName.getSimpleName());
Class[] interfaces = cls.getInterfaces();
for (Class c : interfaces) {
System.out.println(c.getName());
}
}
}
输出结果为:
com.fs.demo01
com.fs.demo01.Demo01
Demo01
Object
java.io.Serializable
类对象属性相关
getFields():获取所有公共属性
getDeclaredFields():获取所有属性
getDeclaredField("属性名"):获取指定属性
Field:属性对应的类
属性对象.get(对象名):获取该对象的该属性
属性对象.set(对象名,属性值):设置属性值
属性对象.setAccessible(true):设置属性略过访问权限修饰符限制
实例:
package com.fs.demo01;
public class Person {
public String name;
public String sex;
private int age;
public Person() {
super();
}
public Person(String name, String sex, int age) {
super();
this.name = name;
this.sex = sex;
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", sex=" + sex + ", age=" + age + "]";
}
}
package com.fs.demo01;
import java.io.Serializable;
import java.lang.reflect.Field;
public class Demo2 implements Serializable{
public static void main(String[] args) throws Exception {
Class cls=Class.forName("com.fs.demo01.Person");
Person person = new Person("张三", "男",18);
System.out.println(person);
Field[] fields = cls.getFields();
System.out.println("getFields().getName()获取到的公共属性的名称:");
for (Field field : fields) {
System.out.println(field.getName());
}
Field sexField = cls.getField("sex");
System.out.println("getField(属性名).getName()获取得到属性对象的属性名:");
System.out.println(sexField.getName());
sexField.set(person, "女");
System.out.println("getField(属性名).set()更改对象的属性值:");
System.out.println(person);
Field[] decl = cls.getDeclaredFields();
System.out.println(".getDeclaredFields(属性名).getName()得到所有属性:");
for (Field field : decl) {
System.out.println(field.getName());
}
Field declAge = cls.getDeclaredField("age");
System.out.println("getDeclaredField(属性名).getName()获取得到属性对象的属性名:");
System.out.println(declAge.getName());
declAge.setAccessible(true);
System.out.println("getDeclaredField(属性名).get(对象名)获取对象的属性值:");
System.out.println(declAge.get(person));
declAge.set(person, 28);
System.out.println("getDeclaredField(属性名).set()更改对象的属性值:");
System.out.println(person);
}
}
输出:
Person [name=张三, sex=男, age=18]
getFields().getName()获取到的公共属性的名称:
name
sex
getField(属性名).getName()获取得到属性对象的属性名:
sex
getField(属性名).set()更改对象的属性值:
Person [name=张三, sex=女, age=18]
.getDeclaredFields(属性名).getName()得到所有属性:
name
sex
age
getDeclaredField(属性名).getName()获取得到属性对象的属性名:
age
getDeclaredField(属性名).get(对象名)获取对象的属性值:
18
getDeclaredField(属性名).set()更改对象的属性值:
Person [name=张三, sex=女, age=28]
类对象方法相关
getMethods():获取所有公共方法的对象,数组
getMethod(name,Class<?>... parameterTypes)通过方法名获取类中方法的对象,仅限公共方法
name:方法名称 parameterTypes:形参列表的数据类型的类对象,若方法没有形参列表则可省略
getDeclaredMethods():获取所有方法
getDeclaredMethod(name,Class... types):获取指定的方法
name:方法名
types:该方法的形参列表对应的类对象
Method(方法对应的类):
方法对象.setAccessible(true):设置属性略过访问权限修饰符限制
方法对象.invoke(对象名,实参列表):调用方法
实例:
具体类:
public class Person {
public String name;
public String sex;
private int age;
public Person() {
super();
}
public Person(String name, String sex, int age) {
super();
this.name = name;
this.sex = sex;
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
private void pfun(String act) {
System.out.println("这是一个私有方法,你可通过该方法进行"+act);
}
@Override
public String toString() {
return "Person [name=" + name + ", sex=" + sex + ", age=" + age + "]";
}
}
方法实现:
import java.lang.reflect.Method;
public class Demo3{
public static void main(String[] args) throws Exception {
Person person = new Person("张三", "男",18);
Class<? extends Person> cls= person.getClass();
System.out.println(person);
Method[] methods = cls.getMethods();
System.out.println("输出公共方法名称,包括继承父类(Object)的方法:");
for (Method method : methods) {
System.out.print(method.getName()+" ");
}
System.out.println("\n");
System.out.println("通过方法名获取类中方法的对象,仅限公共方法:");
Method method = cls.getMethod("toString");
System.out.println(method.getName()+"\n");
Method[] allDecMeth = cls.getDeclaredMethods();
System.out.println("获取所有方法,包含私有方法,不包含继承方法:");
for (Method meth : allDecMeth) {
System.out.print(meth.getName()+" ");
}
System.out.println("\n");
System.out.println("获取得到Person类中的私有方法pfun()");
Method pMethod = cls.getDeclaredMethod("pfun", String.class);
pMethod.setAccessible(true);
pMethod.invoke(person, "吃饭");
}
}
输出:
Person [name=张三, sex=男, age=18]
输出公共方法名称,包括继承父类(Object)的方法:
toString getAge setAge wait wait wait equals hashCode getClass notify notifyAll
通过方法名获取类中方法的对象,仅限公共方法:
toString
获取所有方法,包含私有方法,不包含继承方法:
toString pfun getAge setAge
获取得到Person类中的私有方法pfun()
这是一个私有方法,你可通过该方法进行吃饭
类对象构造函数相关
getConstructors():获取公共的构造函数对象
getDeclaredConstructors():获取所有的构造函数
getDeclaredConstructor(types):获取指定的构造函数
types:该构造函数的形参列表对应的类对象
Constructor:构造函数对应的类
构造函数对象.setAccessible(true):设置属性略过访问权限修饰符限制
构造函数对象.newInstance(实参列表):创建该类的对象
举例:
类
public class Person {
public String name;
public String sex;
private int age;
public Person() {
super();
}
private Person(String name, String sex, int age) {
super();
this.name = name;
this.sex = sex;
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", sex=" + sex + ", age=" + age + "]";
}
}
方法实现:
public class Demo4{
public static void main(String[] args) throws Exception {
Class<? extends Person> cls= Person.class;
System.out.println("获取类对象所拥有的所有公共构造函数的对象:");
Constructor<?>[] constructors = cls.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor.getName());
}
System.out.println("获取所有的构造函数,包括私有构造函数:");
Constructor<?>[] decCons = cls.getDeclaredConstructors();
for (Constructor<?> constructor : decCons) {
System.out.println(constructor.getName());
}
System.out.println("通过形参列表获取指定的构造函数\n之后通过获取到的构造函数创建对象:");
Constructor<? extends Person> decCon =
cls.getDeclaredConstructor(String.class,String.class,int.class);
decCon.setAccessible(true);
Person person = decCon.newInstance("张三","男",36);
System.out.println(person);
}
}
输出:
获取类对象所拥有的所有公共构造函数的对象:
com.fs.demo01.Person
获取所有的构造函数,包括私有构造函数:
com.fs.demo01.Person
com.fs.demo01.Person
通过形参列表获取指定的构造函数
之后通过获取到的构造函数创建对象:
Person [name=张三, sex=男, age=36]
枚举
定义
访问权限修饰符 enum 枚举名{
名1,名2,名3,...
}
注意:枚举中可以定义属性,方法,构造函数,但是没有意义;枚举是一个特殊的类,枚举中必要有枚举属性,所有构造函数都是私有的,枚举属性后的分号可写,可不写;如果在枚举属性下定义由普通方法、属性和构造函数的话必须写分号,但是可不写枚举变量直接写;表示枚举变量书写结束
使用
一般情况:枚举名.名x
switch中:名x
枚举与常量接口比较
枚举可以限制传入的参数的取值范围(当形参为枚举类型时,传入的参数只能为枚举类中的值,而不能是其他值)
设计模式
因为在开发中大量使用,导致程序员对特殊情况的一些处理的方案进行总结
以便于下次遇到该情况可以快速解决的思路
单例
问题:一个类只能产生一个对象
思想:
懒汉式
1,私有化构造函数,此处可以保证无法直接通过new关键字创建该类对象
2,在属性区声明一个该类的对象,使用private修饰
3,提供一个公共的静态方法,在方法中返回唯一的一个该类对象
判断步骤2中声明的对象是否为null
如果为null创建一个对象,赋值给步骤2声明的对象
直接返回步骤2的对象
优点:在不使用的情况下,占据的内存较小
缺点:目前的懒汉式是线程不安全的
举例:
public class LazyMan {
private static LazyMan lazy=null;
private LazyMan() {
}
public static LazyMan getLazy() {
if(lazy==null) {
lazy=new LazyMan();
}
return lazy;
}
}
饿汉式
1,私有化构造函数,此处可以保证无法直接通过new关键字创建该类对象
2,在属性区声明并创建一个该类的对象,使用private static修饰
3,提供一个公共的静态方法,在方法中返回唯一的一个该类对象
返回步骤2的对象
优点:线程安全
缺点:在不使用的情况下,占据的内存较大
举例:
public class HungryMan {
private static HungryMan hm=new HungryMan();
private HungryMan() {
}
public static HungryMan getHm() {
return hm;
}
}
线程安全懒汉式
就是给懒汉式创建对象的公共静态方法使用synchronized修饰
优点:线程安全,在不使用的情况下,占据的内存较小
举例:
public class LazyMan {
private static LazyMan lazy=null;
private LazyMan() {
}
public synchronized static LazyMan getLazy() {
if(lazy==null) {
lazy=new LazyMan();
}
return lazy;
}
}
内部类形式的饿汉式
1,私有化构造函数,此处可以保证无法直接通过new关键字创建该类对象
2,在该类中提供一个静态内部类,在该类属性区声明并创建一个外部类对象
3,提供一个公共的静态方法,该方法中返回步骤2中创建的对象
优点:线程安全,在不使用的情况下,占据的内存较小
举例:
public class HungryMan {
private HungryMan() {
}
static class Gethm{
private static HungryMan hm=new HungryMan();
}
public static HungryMan getHm() {
return Gethm.hm;
}
}
工厂
一个方法可以生成多个数据类型不同的产品
举例:
父类:
public abstract class Dog {
private String type;
public Dog() {
super();
}
public Dog(String type) {
super();
this.type = type;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
@Override
public String toString() {
return "Dog [type=" + type + "]";
}
}
子类:
public class JinMao extends Dog{
public JinMao() {
super("金毛");
}
}
public class TaiDi extends Dog{
public TaiDi() {
super("泰迪");
}
}
工厂类:
public class Factory {
public Dog getDog(Class c) {
Dog dog = null;
try {
Constructor constructor = c.getDeclaredConstructor();
constructor.setAccessible(true);
dog = (Dog) constructor.newInstance();
} catch (NoSuchMethodException | SecurityException e) {
e.printStackTrace();
}finally {
return dog;
}
}
}
测试类:
public class Test {
public static void main(String[] args) {
Factory factory = new Factory();
Dog dog = factory.getDog(JinMao.class);
System.out.println(dog);
Dog dog02 = factory.getDog(TaiDi.class);
System.out.println(dog02);
}
}
通过向工厂传入类对象,工厂通过类对象获取具体类中的构造函数来创建父类对象,然后将父类对象进行返回
总结:
1,提供一个所有产品的父类或父接口
2,让所有产品类可以继承或实现步骤1中的类或接口
3,提供工厂类
4,在工厂类中提供一个公共的方法,该方法的返回值就是步骤1的类或接口对象
方式1:枚举或常量接口(通过switch进行选择)
判断该方法传入的参数与枚举或常量接口中的那个值相同
创建对应的对象,并返回
方式2:使用反射机制
使步骤4的形参为需要创建的产品的类的类对象
使用类对象获取该类的构造函数对象
使用构造函数对象创建该类对象对应的类的对象
注解
概念
注解:
解释代码的代码就是注解(元代码)
元注解:
解释注解的注解
常用注解
@Override
表示对父类方法进行了重写
@Deprecated
表示该方法、该类、该属性已经过时了,有更新更好的替代了
@SuppressWarnings
忽略类、字段、方法、参数、构造方法的警告
使用:
@SuppressWarnings("all")
@SuppressWarnings("unchecked")
@SuppressWarnings("unused")
@SuppressWarnings("resource")
@SuppressWarnings("path")
@SuppressWarnings("deprecation")
@SuppressWarnings("fallthrough")
@SuppressWarnings("serial")
@SuppressWarnings("rawtypes")
@SuppressWarnings({"警告类型1","警告类型2".....})
自定义注解
访问权限修饰符 @interface 注解名{
public 数据类型 属性名();
public 数据类型 属性名() default 默认值;
}
注意:
如果定义的注解中的属性没有默认值,使用该注解时,必须传入值
如果定义的注解中属性有默认值,在使用该注解时,可以选择是否需要传入
注解的属性默认使用public修饰,也只能public修饰
注解的使用
可以在类,接口,方法,属性,局部变量,构造函数...上使用
语法规则:
@注解名
注意:只能使用没有属性的注解或注解中所有属性均有默认值
@注解名(属性名1 = 值1,属性名2=值2)
注意:此处属性名顺序可以与定义注解时属性不一致
@注解名(值)
注意:只有注解中的属性名为value时可以省略属性名不写
注意:如果注解中的属性有默认值,可以在使用注解时不用给其中传入参数
元注解
概念:解释注解的注解
常用元注解:
@Documented:生成文档
@Retention:注解起作用的时机
1,代码编写:RetentionPolicy.SOURCE
2,代码编译与代码编写:RetentionPolicy.CLASS
3,代码运行,代码编译,代码编写RetentionPolicy.RUNTIME
@Target:限定注解的使用位置
ElementType.TYPE:可以在类,接口,枚举上用
ElementType.FIELD:可以在属性上使用
ElementType.METHOD:可以在方法上使用
ElementType.PARAMETER:可以在形参前使用
ElementType.CONSTRUCTOR:可以在构造函数上使用
ElementType.LOCAL_VARIABLE:可以在局部变量上使用
ElementType.ANNOTATION_TYPE:可以在注解上使用
ElementType.PACKAGE:可以在包上使用
注意:如果注解上没有该注解,说明该注解可以在任何地方使用,因为默认值all
@Inherited:注解是否可以被继承
默认是不能被继承的,加上该注解就可以被继承了
注解与反射的结合
获取的注解被元注解@Retention(RetentionPolicy.RUNTIME)进行修饰,否则获取得到的注解对象为空。
注解类型 注解对象 = 类对象.getAnnotation(注解的类对象):获得指定的注解对象
使用注解对象.属性名():获取注解中的属性值
举例:
注解:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
public int test();
public String error() default "ERROR";
}
测试类:
@MyAnnotation(test=404)
public class Test {
public static void main(String[] args) {
Class<Test> te=Test.class;
MyAnnotation annotation =te.getAnnotation(MyAnnotation.class);
System.out.println(annotation);
}
}
输出结果:
@com.fs.intface.MyAnnotation(error=ERROR, test=404)