反射机制
1. 反射机制的作用
通过java语言中的反射机制可以操作字节码文件。优点类似于黑客。(可以读和修改字节码文件。)通过反射机制可以操作代码片段。(class文件。)
反射机制相关的重要的类
- java.lang.Class: 代表整个字节码,代表一个类型,代表整个类。
- java.lang.reflect.Method: 代表字节码中的方法字节码。代表类中的方法。
- java.lang.reflect.Constructor: 代表字节码中的构造方法字节码。代表类中的构造方法。
- java.lang.reflect.Field: 代表字节码中的属性字节码。代表类中的成员变量(静态变量)。
2 Class
获取class的三种方式
Class.forName(“完整类名带包名”)方法
- 静态方法
- 方法的参数是一个字符串。
- 字符串需要的是一个完整类名。
- 完整类名必须带有包名。java.Lang包也不能省略。
package com.reflect;
public class ReflectTest01 {
public static void main(String[] args) {
try {
Class c1 = Class.forName("java.lang.String");
Class c2 = Class.forName("java.lang.Integer");
Class c3 = Class.forName("java.util.Date");
Class c4 = Class.forName("java.lang.System");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
对象.getclass()方法
java中任何一个对象都有一个方法:getclass()
//java中任何一个对象都有一个方法:getclass()
String s = "abc";
Class x = s.getClass();
System.out.println(x == c1); //true
类型.class属性
package com.reflect;
import java.util.Date;
public class ReflectTest01 {
public static void main(String[] args) {
Class c1 = null;
try {
c1 = Class.forName("java.lang.String");
Class c2 = Class.forName("java.lang.Integer");
Class c3 = Class.forName("java.util.Date");
Class c4 = Class.forName("java.lang.System");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//java中任何一个对象都有一个方法:getclass()
String s = "abc";
Class x = s.getClass();
System.out.println(x == c1); //true
//第三种方式,java语言中任何一种类型,包括基本数据类型,它都有.class属性。
Class z = String.class;//z代表string类型
Class k = Date.class;//k代表Date类型
Class f = int.class;//f代表int类型
Class e = double.class;//e代表double类型
System.out.println(x==z);
}
}
通过反射实例化对象
package com.reflect;
public class ReflectTest02 {
public static void main(String[] args) {
//通过反射机制,获取CLass,通过CLass来实例化对象
try {
Class c = Class.forName("com.reflect.bean.User");
//newInstance()这个方法会调用User这个类的无参数构造方法,完成对象的创建。
//重点是:newInstance()调用的是无参构造,必须保证无参构造是存在的!
Object obj = c.newInstance();
System.out.println(obj);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
验证反射机制的灵活性
java代码写一遍,再不改变java源代码的基础之上,可以做到不同对象的实例化。非常之灵活。(符合OCP开闭原则:对扩展开放,对修改关闭。)
后期你们要学习的是高级框架,而工作过程中,也都是使用高级框架,包括:ssh ssm
Spring SpringMVC MyBatis
Spring Struts Hibernate
…
这些高级框架底层实现原理:都采用了反射机制。所以反射机制还是重要的。学会了反射机制有利于你理解剖析框架底层的源代码。
package com.reflect;
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;
/*
验证反射机制的灵活性
*/
public class ReflectTest03 {
public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
//以下代码是灵活的,代码不需要改动,可以修改配置文件,配置文件修改之后,可以创建出不同的实例对象。
//通过IO流读取classinfo.properties文件
FileReader reader = new FileReader("classinfo.properties");
//创建属性类对象Map
Properties pro = new Properties();
//加载
pro.load(reader);
//关闭流
reader.close();
//通过key获取value
String className = pro.getProperty("className");
System.out.println(className);
//通过反射机制实例化对象
Class c = Class.forName(className);
Object obj = c.newInstance();
System.out.println(obj);
}
}
CLass.forName()发生了什么?
重点:
如果只是希望一个类的静态代码块执行,其它代码一律不执行,可以使用:
CLass.forName("完整类名");
这个方法的执行会导致类加载,类加载时,静态代码块执行。
提示:
JDBC技术可能需要。
获取类路径下的文件绝对路径
package com.reflect;
import java.io.FileNotFoundException;
import java.io.FileReader;
public class AboutPath {
public static void main(String[] args) throws FileNotFoundException {
//这种方式的路径缺点是:移植性差,在IDEA中默认的当前路径是project的根。
//这个代码假设离开了IDEA,换到了其它位置,可能当前路径就不是project的根了,这时这个路径就无效了。
//FileReader reader = new FileReader("classinfo2.properties");
//通用方式
//只适用文件在类路径下,在src下的都是在类路径下
//src是类的根路径
/*解释:
Thread.currentThread()当前线程对象
getContextClassLoader()是线程对象的方法,可以获取到当前线程的类加载器对象。
getResource()【获取资源】这是类加载器对象的方法,当前线程的类加载器默认从类的根路径下加载资源。*/
///D:/OneDrive%20-%20guyuer/Java/JavaSE/out/production/JavaSE/classinfo.properties
String path = Thread.currentThread().getContextClassLoader().getResource("classinfo.properties").getPath();
System.out.println(path);
String path1 = Thread.currentThread().getContextClassLoader()
.getResource("com/reflect/bean/db.properties").getPath();
System.out.println(path1);
///D:/OneDrive%20-%20guyuer/Java/JavaSE/out/production/JavaSE/com/reflect/bean/db.properties
}
}
以流的方式返回
package com.reflect;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class IoPropertiesTest {
public static void main(String[] args) throws IOException {
//获取一个文件的绝对路径了!!!!!
/*String path=Thread.currentThread().getContextclassLoader()
.getResource("classinfo2.properties").getPath();
FileReader reader = new FileReader(path);*/
//直接以流的形式返回。
InputStream reader = Thread. currentThread(). getContextClassLoader()
.getResourceAsStream("classinfo.properties");
Properties pro = new Properties();
pro.load(reader);
reader.close();
//通过key获取value
String className = pro.getProperty("className");
System.out.println(className);
}
}
资源绑定器
package com.reflect;
import java.util.ResourceBundle;
/*
java.util包下提供了一个资源绑定器,便于获取属性配置文件中的内容。
使用以下这种方式的时候,属性配置文件xxx.properties必须放到类路径下。
*/
public class ResourceBundleTest {
public static void main(String[] args) {
//资源绑定器,只能绑定xxx.properties文件。并且这个文件必须在类路径下。文件扩展名也必须是properties
//并且在写路径的时候,路径后面的扩展名不能写。
ResourceBundle bundle = ResourceBundle.getBundle("classinfo");
String className = bundle.getString("className");
System.out.println(className);
}
}
类加载器
关于JDK中自带的类加载器:
-
什么是类加载器?
专门负责加载类的命令/工具。
ClassLoader
-
JDK中自带了3个类加载器
- 启动类加载器
- 扩展类加载器
- 应用类加载器
-
假设有这样一段代码:
string s = " abc " ;
代码在开始执行之前,会将所需要类全部加载到JVM当中。
通过类加载器加载,看到以上代码类加载器会找string.class文件,找到就加载,那么是怎么进行加载的呢?
- 首先通过、启动类加载器"加载。
注意:启动类加载器专门加载:C:\Program Files\Java\jdk1.8.0_101\jre\lib\rt.jar
rt.jar中都是JDK最核心的类库。
- 如果通过“启动类加载器"加载不到的时候,会通过“扩展类加载器“加载。
注意:扩展类加载器专门加载:c:\Program Files\Java\jdk1.8.0_101\jre\lib\ext\*.jar
- 如果,扩展类加载器"没有加载到,那么会通过应用类加载器”加载。
注意:应用类加载器专门加载:classpath中的类。
双亲委派机制
java中为了保证类加载的安全,使用了双亲委派机制。优先从启动类加载器中加载,这个称为父",“父"无法加载到,再从扩展类加载器中加载,这个称为"母”。
双亲委派。如果都加载不到,才会考虑从应用类加载器中加载。直到加载到为止。
3 Method
可变长度参数
可变长度参数
int... args
//语法是:类型...(注意:一定是3个点。)
package com.reflect;
/*
可变长度参数
int...args这就是可变长度参数
语法是:类型...(注意:一定是3个点。)
*/
public class ArgsTest {
public static void main(String[] args) {
m();
m(10);
m(10,20);
m(10,20,30,40); //只能int
String[] strs = {"a","b","c"};
//也可以传1个数组
m2(strs);
}
public static void m(int... args){
System.out.println("m方法执行了");
}
public static void m2(String... args){
for (int i = 0; i < args.length; i++) {
System.out.println(args[i]);
}
}
}
用法(了解)
类比Field
通过反射机制调用对象方法(重点)
重点:必须掌握,通过反射机制怎么调用一个对象的方法?
反射机制,让代码很具有通用性,可变化的内容都是写到配置文件当中,将来修改配置文件之后,创建的对象不一样了,调用的方法也不同了,但是java代码不需要做任何改动。这就是反射机制的魅力。|
package com.reflect;
import com.reflect.bean.UserService;
import java.lang.reflect.Method;
public class ReflectTest10 {
public static void main(String[] args) throws Exception {
//不使用反射机制,怎么调用方法
//创建对象
UserService userService = new UserService();
//调用方法
/*要素分析:
要素1:对象UserService
要素2:Login方法名
要素3:实参列表
要素4:返回值*/
boolean loginSuccess = userService.login("admin","123");
//System.out.println(LoginSuccess);
System.out.println(loginSuccess?"登录成功":"登录失败");
//使用反射机制来调用一个对象的方法该怎么做?
Class userServiceClass = Class.forName("com.reflect.bean.UserService");
//创建对象
Object obj = userServiceClass . newInstance();
//获取Method
Method loginMethod = userServiceClass . getDeclaredMethod ("login",String.class,String.class);
/*
四要素:
LoginMethod方法
obj对象
"admin","123"实参
retValue 返回值
*/
//调用方法
Object retValue = loginMethod.invoke(obj, "admin", "123");
System.out.println(retValue);
}
}
4 Constructor
Constructor的使用
反编译类的Constructor
package com.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
/*
反编译类的Constructor
*/
public class ReflectTest11 {
public static void main(String[] args) throws ClassNotFoundException {
StringBuilder s = new StringBuilder();
Class vipClass = Class.forName("com.reflect.bean.Vip");
s.append(Modifier.toString(vipClass.getModifiers()));
s.append(" class ");
s.append(vipClass.getSimpleName());
s.append("{\n");
//拼接构造方法
Constructor[] constructors = vipClass.getDeclaredConstructors();
for (Constructor constructor : constructors) {
s.append("\t");
s.append(Modifier.toString(constructor.getModifiers()));
s.append(" ");
s.append(vipClass.getSimpleName());
s.append("(");
Class[] parameterTypes = constructor.getParameterTypes();
for (Class parameterType : parameterTypes) {
s.append(parameterType.getSimpleName());
s.append(",");
}
if (parameterTypes.length > 0) {
s.deleteCharAt(s.length() - 1);
}
s.append("){}\n");
}
s.append("}");
System.out.println(s);
}
}
/*
package com.reflect.bean;
public class Vip {
int no;
String name;
String birth;
boolean sex;
public Vip(){
}
public Vip(int no) {
this.no = no;
}
public Vip(int no, String name) {
this.no = no;
this.name = name;
}
public Vip(int no, String name, String birth) {
this.no = no;
this.name = name;
this.birth = birth;
}
public Vip(int no, String name, String birth, boolean sex) {
this.no = no;
this.name = name;
this.birth = birth;
this.sex = sex;
}
}
*/
通过反射机制调用构造方法
package com.reflect;
import com.reflect.bean.Vip;
import java.lang.reflect.Constructor;
//通过反射机制调用构造方法实例化java对象。
public class ReflectTest12 {
public static void main(String[] args) throws Exception {
//不使用反射机制怎么创建对象
Vip v1 = new Vip();
Vip v2 = new Vip(1111, "yy");
System.out.println(v2);
//使用反射机制怎么创建对象呢?
Class c = Class.forName("com.reflect.bean.Vip");
//调用无参数构造方法
c.newInstance();
//调用有参数的构造方法怎么办?
//第一步:先获取到这个有参数的构造方法
Constructor con = c.getDeclaredConstructor(int.class, String.class, String.class, boolean.class);
Object v3 = con.newInstance(110, "gg", "1990-1-1", true);
System.out.println(v3);
//获取无参数构造方法
Constructor con2 = c.getDeclaredConstructor();
Object v4 = con2.newInstance();
System.out.println(v4);
}
}
获取父类和父接口
package com.reflect;
/*
重点:给你一个类,怎么获取这个类的父类,已经实现了哪些接口?
*/
public class ReflectTest13 {
//String举例
public static void main(String[] args) throws Exception {
Class stringClass = Class.forName("java.lang.String");
//获取string的父类
Class superClass = stringClass.getSuperclass();
System.out.println(superClass.getName());
//获取string类实现的所有接口(一个类可以实现多个接口。)
Class[] interfaces = stringClass.getInterfaces();
for (Class in : interfaces){
System.out.println(in.getName());
}
}
}
5 Field
field:属性
package com.reflect;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class ReflectTest05 {
public static void main(String[] args) throws ClassNotFoundException {
//获取整个类
Class studentClass = Class.forName("com.reflect.bean.Student");
//获取类名
String className = studentClass.getName();
System.out.println("完整类名"+className);
String simpleName = studentClass.getSimpleName();
System.out.println("简类名"+simpleName);
//获取类中所有的Field,只能获得public修饰的属性
Field[] fields = studentClass.getFields();
System.out.println(fields.length); //1
//取出这个Field
Field field = fields[0];
//获取field的名字
String fieldName = field.getName();
System.out.println(fieldName); //no
//获取所有的属性
Field[] declaredFields = studentClass.getDeclaredFields();
System.out.println(declaredFields.length); //4
System.out.println("=================================");
for(Field fd : declaredFields){
//获取属性修饰符列表
int modifiers = fd.getModifiers(); //返回修饰符的代号(数字)
String s = Modifier.toString(modifiers);
System.out.print(s+" ");
//获取属性类型
Class fdType = fd.getType();
String fdTypeName = fdType.getSimpleName();
System.out.print(fdTypeName+" ");
//获取属性名字
System.out.println(fd.getName());
}
}
}
/*
package com.reflect.bean;
//反射属性:字段
public class Student {
public int no;
private String name;
protected int age;
boolean sex;
public static final double MATH_PI = 3.1415926;
}
*/
反射属性
反编译
package com.reflect;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
/*
反编译
反射机制的强大功能
*/
public class ReflectTest06 {
public static void main(String[] args) throws ClassNotFoundException {
//准备拼接字符串
StringBuilder s = new StringBuilder();
//Class studentClass = Class.forName("com.reflect.bean.Student");
//Class studentClass = Class.forName("java.lang.String");
Class studentClass = Class.forName("java.lang.Integer");
s.append(Modifier.toString(studentClass.getModifiers())+" class "+studentClass.getSimpleName()+" {\n");
Field[] fields = studentClass.getDeclaredFields();
for (Field fd : fields){
s.append("\t");
s.append(Modifier.toString(fd.getModifiers()));
s.append(" ");
s.append(fd.getType().getSimpleName());
s.append(" ");
s.append(fd.getName());
s.append(";\n");
}
s.append("}");
System.out.println(s);
}
}
反射获取和赋值
package com.reflect;
import java.lang.reflect.Field;
/*
必须掌握:
怎么通过反射机制访问一个java对象的属性?
给属性赋值set
获取属性的值get
*/
public class ReflectTest07 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
Class studentClass = Class.forName("com.reflect.bean.Student");
Object obj = studentClass.newInstance();
// Field[] fields = studentClass.getDeclaredFields();
//获取no属性
Field no = studentClass.getField("no");
//属性赋值
/*虽然使用了反射机制,但是三要素还是缺一不可:
要素1:obj对象
要素2:no属性
要素3:2222值
注意:反射机制让代码复杂了,但是为了一个“灵活”,这也是值得的。
*/
no.set(obj,2222);
//获取属性值
System.out.println(no.get(obj));
//私有属性,打破封装才能get,(反射机制的缺点:打破封装,可能会给不法分子留下机会!!!)
Field name = studentClass.getDeclaredField("name");
name.setAccessible(true);
name.set(obj,"gu");
System.out.println(name.get(obj));
}
}
注解
1 注解的定义和使用
定义
注解,或者叫做注释类型,英文单词是:Annotation
注解Annotation是一种引用数据类型。编译之后也是生成xxx.class文件。
怎么自定义注解呢?语法格式?
[修饰符列表]@interface注解类型名{
}
package com.annotation;
public @interface MyAnnotation {
}
使用
注解怎么使用,用在什么地方?
第一:注解使用时的语法格式是:
@注解类型名
第二:注解可以出现在类上、属性上、方法上、变量上等…
注解还可以出现在注解类型上。
package com.annotation;
@MyAnnotation
public class AnnotationTest01 {
@MyAnnotation
private int no;
@MyAnnotation
public static void m1(){
@MyAnnotation
int i = 100;
}
@MyAnnotation
public void m2(){
}
}
@MyAnnotation
interface MyInterface{
}
@MyAnnotation
enum Season{
SPRING,SUMMER,AUTUMN,WINTER
}
2 JDK内置的注解
java.lang包下的注释类型:
掌握:
Deprecated用@Deprecated注释的程序元素,不鼓励程序员使用这样的元素,通常是因为它很危险或存在更好的选择。掌握:
override表示一个方法声明打算重写超类中的另一个方法声明。不用掌握:
suppresswarnings 指示应该在注释元素(以及包含在该注释元素中的所有程序元素)中取消显示指定的编译器警告。
override注解-重写
关于JDK Lang包下的override注解
源代码:
public @interface Override {
}
标识性注解,给编译器做参考的。
编译器看到万法上有这个注解的时候,编译器会自动检查该万法是否重写了父类的万法。
如果没有重写,报错。
这个注解只是在编译阶段起作用,和运行期无关!
Deprecated注解-已过时
package com.annotation;
//表示这个类已过时。
public class AnnotationTest03 {
public static void main(String[] args) {
AnnotationTest03 at = new AnnotationTest03();
at.doSome();
}
@Deprecated
public void doSome() {
System.out.println("do something!");
}
//Deprecated这个注解标注的元素已过时。
//这个注解主要是向其它程序员传达一个信息,告知已过时,有更好的解决方案存在。
@Deprecated
public static void doOther() {
System.out.println("do other...");
}
}
class T {
public static void main(String[] args) {
AnnotationTest03 at = new AnnotationTest03();
at.doSome();
}
}
元注解
标注注解类型的注解
常见的元注解有哪些?
-
Target
-
Retention
关于Target注解:
这是一个元注解,用来标注"注解类型"的"注解"
这个Target注解用来标注被标注的注解”可以出现在哪些位置上。
@Target(ElementType.METHOD):表示被标注的注解"只能出现在方法上。
关于Retention注解:
这是一个元注解,用来标注"注解类型"的"注解"
这个Retention注解用来标注被标注的注解”最终保存在哪里。
@Retention(RetentionPolicy.SOURCE):注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
@Retention(RetentionPolicy.CLASS):注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
@Retention(RetentionPolicy.RUNTIME):注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
3 自定义注解
使用属性
package com.annotation2;
public class MyAnnotationTest01 {
//报错的原因:如果一个注解当中有属性,那么必须给属性赋值。(除非该属性使用default指定了默认值)
/*
@MyAnnotation
public void doSome(){
}
*/
//指定name属性的值就好了。
@MyAnnotation(name = "zhangsan",color = "red")
public void doSome(){
}
}
/*
package com.annotation2;
public @interface MyAnnotation {
/**
* 我们通常在注解当中可以定义属性,以下这个是MyAnnotation的name属性。
* 看着像1个方法,但实际上我们称之为属性name。
* @return
*/
/*
String name();
String color();
int age() default 20; //指定默认值,可以在用用注解的地方写,也可以不写默认
}
*/
value属性
只有一个属性,且属性名是value时,使用时可以省略属性名
@MyAnnotation(value="hehe")
public void doSome(){
}
@MyAnnotation("haha")
public void doother(){
}
注解属性的类型
属性的类型可以是:
byte short int Long float double boolean char String Class 枚举类型,以及以上每一种的数组形式。
如果数组中只有1个元素:大括号可以省略。
判断类上面是否有注解
//判断类上面是否有@MyAnnotation
System.out.println(c.isAnnotationPresent(MyAnnotation.class));
if(c.isAnnotationPresent(MyAnnotation.class)){
//获取该注解对象
MyAnnotation myAnnotation=(MyAnnotation)c.getAnnotation(MyAnnotation.class);
System.out.println("类上面的注解对象"+myAnnotation);
}
4 实际开发注解作用
需求:
假设有这样一个注解,叫做:@Id这个注解只能出现在类上面,当这个类上有这个注解的时候,
要求这个类中必须有一个int类型的id属性。如果没有这个属性
就报异常。如果有这个属性则正常执行!
package com.annotation7;
import java.lang.reflect.Field;
public class Test {
public static void main(String[] args) throws Exception {
Class c = Class.forName("com.annotation7.User");
//判断类上是否存在Id注解
boolean isRight = false;
if(c.isAnnotationPresent(Id.class)){
Field[] declaredFields = c.getDeclaredFields();
for (Field file : declaredFields){
if("id".equals(file.getName()) && "int".equals(file.getType().getSimpleName())){
//表示这个类是合法的类。有@Id注解,则这个类中必须有int类型的id
isRight = true;
break;
}
}
//判断
if (!isRight){
throw new HasNotIdPropertyException("被Id注解标注的类中必须有一个int类型的id属性");
}
}
}
}
/*
package com.annotation7;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//只能出现在类上
@Target(ElementType.TYPE)
//可以被反射机制读到
@Retention(RetentionPolicy.RUNTIME)
public @interface Id {
}
package com.annotation7;
@Id
public class User {
//int id;
String name;
String password;
}
package com.annotation7;
public class HasNotIdPropertyException extends RuntimeException{
public HasNotIdPropertyException() {
}
public HasNotIdPropertyException(String message) {
super(message);
}
}
*/