JAVA学习之-反射

一,类对象

  • 类的对象:基于某个类new出来的对象,也叫实例对象
  • 类对象:类加载的产物,封装了一个类所有的信息(类名,父类,接口,属性,方法,构造方法)Java源代码编译后的.class文件存储在硬盘中,从硬盘加载进内存。就是一个类对象
  • 将类的各个组成部分封装为其他对象,就是反射机制

反射的好处:

            1. 可以在程序运行过程中,操作这些对象。
            2. 可以解耦,提高程序的可扩展性。

1.1Java代码在计算机中的三个阶段

JAVA学习之-反射

 

 1.2 获取Class对象的方式:

  • Class.forName("包名.类名"):将字节码文件加载进内存,返回Class对象    * 多用于配置文件,将类名定义在配置文件中。读取文件,加载类    推荐写法
  • 类名.class:通过类名的属性class获取,返回Class对象   * 多用于参数的传递
  • 对象.getClass():getClass()方法在Object类中定义着。  * 多用于对象的获取字节码的方式
  1. 同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。

通用操作:

  • 使用反射机制获取类对象,并使用Class对象的方法获取表示类成员的各种对象(Constructor,Method,Field),实现反射各种应用

JAVA学习之-反射

案例,

package com.qf;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Properties;

public class TestStudent {
    
    public static void main(String[] args) throws Exception {
        //getClazz();
        //getClazzInfo();
//        getConstructors();
        // getMethods();
        
//        Properties properties=new Properties();
//        ArrayList<String> arrayList=new ArrayList<String>();
//        //properties.setProperty(key, value)
//        properties.setProperty("username", "张三");
//        invokeAny(properties, "setProperty", new Class<?>[] {String.class,String.class}, new Object[] {"username","张三"});
//        invokeAny(arrayList, "add", new Class<?>[] {Object.class}, new Object[] {"梓源"});
//        System.out.println(properties);
//        System.out.println(arrayList);
        
        //getFields();
        testIntrospector();
        
    }
    
    //获取类对象
    public static void getClazz() throws Exception {
        //(1)通过类的对象,调用getClass()方法
        Student xiangrui=new Student();
        Class<?> class1= xiangrui.getClass();
        System.out.println(class1.hashCode());
        //(2)通过类名.class
        Class<?> class2=Student.class;
        System.out.println(class2.hashCode());
        //(3)通过Class.forName()静态方法获取,参数是类的全名称, 推荐写法:(1)灵活 (2)耦合性低
        Class<?> class3=Class.forName("com.qf.Student");
        System.out.println(class3.hashCode());
    }
    //获取类名、包、父类、实现接口
    public static void getClazzInfo() throws Exception {
        //1获取类对象
        Class<?> class1=Class.forName("com.qf.Student");
        //2获取类的全名称
        System.out.println(class1.getName());
        //3获取包信息
        Package package1 = class1.getPackage();
        System.out.println(package1.getName());
        //4获取父类
        Class<?> superclass = class1.getSuperclass();
        System.out.println(superclass.getName());
        System.out.println("-----------------");
        //5获取实现接口
        Class<?>[] interfaceClasses=class1.getInterfaces();
        for (Class<?> class2 : interfaceClasses) {
            System.out.println(class2);
        }
    }
    
    //获取类的构造方法【重点】
    public static void getConstructors() throws Exception {
        //1 获取类对象
        Class<?> class1=Class.forName("com.qf.Student");
        //2获取构造方法
        //2.1getConstructors()获取所有的构造方法
//        Constructor<?>[] constructors=class1.getConstructors();
//        for (Constructor<?> constructor : constructors) {
//            System.out.println(constructor);
//        }
        //2.2获取无参构造方法
        Constructor<?> constructor=class1.getConstructor();
        //使用无参构造方法创建对象
        Student student=(Student)constructor.newInstance();
        System.out.println(student);
        //2.3获取带参构造方法
        Constructor<?> constructor2=class1.getConstructor(String.class,int.class,String.class,String.class);
        //使用带参构造方法创建对象
        Student xiangrui=(Student)constructor2.newInstance("祥睿",20,"1001","男");
        System.out.println(xiangrui);
        
        //3创建对象的简便方法(默认是无参构造方法)
        Student student2=(Student)class1.newInstance();
        System.out.println(student2);
    
    }

    //获取类的普通方法【重点】
    public static void getMethods() throws Exception{
        //获取类对象
        Class<?> class1=Class.forName("com.qf.Student");
        //1获取所有方法 getMethods()获取所有公开的方法,包括继承的公开方法
        //Method[] methods=class1.getMethods();
        //2获取所有方法 getDeclaredMethods(); 获取类中所有的方法,包括私有、保护、默认方法、不包括继承的方法。
//        Method[] methods=class1.getDeclaredMethods();
//        for (Method method : methods) {
//            System.out.println(method);
//        }
        //3获取show方法
        Method methodShow=class1.getMethod("show");
        //调用方法
//        Student student=new Student();
//        student.show();
        Student student=(Student)class1.newInstance();
        methodShow.invoke(student);//student.show();
        
        //获取带参show方法
        Method methodShow2=class1.getMethod("show",String.class);
        methodShow2.invoke(student, "110");
        
        //获取getName()方法
        Method methodGetName=class1.getMethod("getName");
        String name=(String)methodGetName.invoke(student);
        System.out.println(name);
        
        //获取静态方法
        Method methodPrintInfo=class1.getMethod("printInfo");
        methodPrintInfo.invoke(null);//Student.printInfo();
        //获取私有方法
        Method methodPrintInfo2=class1.getDeclaredMethod("printInfo2");
        //设置访问权限无效(不要用,破坏封装性)
        methodPrintInfo2.setAccessible(true);
        methodPrintInfo2.invoke(student);
        
    }

    //通用方法
    public static Object invokeAny(Object obj,String methodName,Class<?>[] paramsType,Object... paramsValue) throws Exception {
        Class<?> class1=obj.getClass();
        Method method=class1.getMethod(methodName, paramsType);
        return method.invoke(obj, paramsValue);
    }
    
    //使用反射操作属性
    public static void getFields() throws Exception {
        //获取类对象
        Class<?> class1 = Class.forName("com.qf.Student");
        //获取属性
        //getFields() 获取公开的属性、从父类继承的属性
        //class1.getFields()
//        Field[] fields=class1.getDeclaredFields();
//        for (Field field : fields) {
//            System.out.println(field);
//        }
        //获取name
        Field fieldName=class1.getDeclaredField("name");
        Student ziyuan = (Student)class1.newInstance();
        
        //调用
        fieldName.setAccessible(true);
        fieldName.set(ziyuan, "梓源");
        System.out.println(fieldName.get(ziyuan));
        
        Field fieldAge=class1.getDeclaredField("age");
        fieldAge.setAccessible(true);
        fieldAge.set(ziyuan, 20);
        System.out.println(fieldAge.get(ziyuan));
    
    }
    
    //内省机制:使用反射技术操作属性的一种机制。
    //Introspector 内省工具类
    //BeanInfo 类信息
    //PropertyDescriptor 属性描述符(代表一个属性)
    //以getXxx  setXxx  isXxx开头的方法叫属性
    
    public static void testIntrospector() throws Exception {
        Class<?> class1=Class.forName("com.qf.Teacher");
        //BeanInfo 类信息
        //BeanInfo beanInfo = Introspector.getBeanInfo(class1);
        //PropertyDescriptor 
        //以getXxx  setXxx  isXxx开头的方法叫属性
//        PropertyDescriptor[] propertyDescriptors=beanInfo.getPropertyDescriptors();
//        for (PropertyDescriptor pd : propertyDescriptors) {
//            System.out.println(pd.getName());
//        }
        
        Teacher lilaoshi=(Teacher)class1.newInstance();
        //获取属性描述符
        PropertyDescriptor pdName=new PropertyDescriptor("name", class1);
        PropertyDescriptor pdAge=new PropertyDescriptor("age", class1);
        PropertyDescriptor pdSalary=new PropertyDescriptor("salary", class1);
        
        //赋值
        pdName.getWriteMethod().invoke(lilaoshi, "李老师");//setName();
        pdAge.getWriteMethod().invoke(lilaoshi, 40);
        pdSalary.getWriteMethod().invoke(lilaoshi, 50000);
        
        System.out.println(lilaoshi);
        
        System.out.println(pdName.getReadMethod().invoke(lilaoshi));
        
    }
    
}

 1.2 java创建对象的四种方式:

  • 使用new关键字,调用构造方法
  • 使用反射技术,调用构造方法
  • 使用Cloneable接口克隆对象,不会调用构造方法,必须实现接口重写clone方法
  • 使用反序列化,不会调用构造方法,实现serializable接口

1.3 设计模式

  • 特定问题的固定解决办法,代码设计经验的总结
  • Gof设计模式中,介绍了23种
  • 好处是可重用,

1.4 工厂设计模式

  • 开闭原则,对拓展开放,对修改关闭
  • 工厂模式主要负责对象的创建问题
  • 可通过反射进行工厂模式的设计,动态完成对象创建

 1.5 单例设计模式

1.5.1饿汉式写法 :缺点:声明周期比较长,浪费空间。 优点:线程安全

  • 把类的构造方法变为私有的。
  • 在单例类内部类创建对象并实例化
  • 在单例类中添加一个公开的方法,返回这个对象
 1 //饿汉式
 2 public class SingleTon1{
 3     private SingleTon1(){
 4 
 5     }
 6     private static final SingleTon1 instance = new SingleTon1();
 7    
 8   public static SingleTon1 getInstance(){
 9       return instance;
10     }
11 }

1.5.2 懒汉式写法:懒汉式写法: 优点:声明周期短,节省空间  缺点:线程不安全

  • 把类的构造方法变为私有的。
  • 在单例类内部类创建对象,没有初始化。
  • 在单例类中添加一个公开的方法,返回这个对象
public class SingleTon2 {
    // (1)把类的构造方法变为私有的。
    private SingleTon2() {
    
    }
    //(2)在单例类内部类创建对象,没有初始化。
    private static SingleTon2 instance=null;
    
    //(3)在单例类中添加一个公开的方法,返回这个对象
    
    public static SingleTon2 getInstance() { //100线程 ,只要有一个线程进入到synchronized中,instance一定实例化了,另外99个线程不需要判断synchronized
        if(instance==null) {//提高效率 另外99判断if
            synchronized (SingleTon2.class) {
                if(instance==null) {
                    instance=new SingleTon2();
                }
            }
        }
        return instance;
    }
}

1.5.3 静态内部类写法

public class SingleTon3 {
    //1私有化构造方法
    private SingleTon3() {
        // TODO Auto-generated constructor stub
    }
    //2创建对象(1 静态内部类不使用,不会提前加载,声明周期短,2 JVM实例化对象时解决线程安全问题)
    private static class Holder{
        private static final SingleTon3 instance=new SingleTon3();
    }
    //3返回这个对象
    public static SingleTon3 getInstance() {
        return Holder.instance;
    }
}

二,枚举

  • 引用类型,规定了取值范围的数据类型
  • 枚举变量不能使用其他的数据,只能使用枚举中的常量赋值,提高程序安全性
  • 定义枚举使用enum关键字
  • 枚举的本质:
  1. 是一个终止类,继承Enum抽象类
  2. 枚举中常量是当前类型的静态常量
/**
 * 定义枚举
 * 关键字enum  (Enumeration)
 * 包含 
 * 1 【常量】 ;可以省略
 * 2 【私有构造方法、属性、方法】必须在常量的后面写
 * @author wgy
 *
 */
public enum Gender {

    //1【常量】
    MALE,FEMALE;
    
    //2 【私有构造方法、属性、方法】必须在常量的后面写
    private Gender() {
        
    }
    private int value;
    
    public void show() {
        System.out.println(value);
    }

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }

}

三,注解

  • 注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
  • 程序可以读取注解,一般用于代替配置文件
  • 通过反射技术获取注解,决定怎么运行类

3.1 定义注解

@interface关键字,注解中只能包含属性

3.2 注解属性类型

1 基本类型
 2 String
 3 Class类型
 4 枚举类型
 5 注解类型
以及上述类型的一维数组

@Retention(value=RetentionPolicy.RUNTIME)
//@Target({ElementType.METHOD,ElementType.TYPE,ElementType.FIELD})
public @interface MyAnnotation {
    //public 属性
    String name() default "张三";
    int age();
    String address();
    
}

3.3 元注解

用来描述注解的注解

 

上一篇:mysql学习--表列的增删改


下一篇:25k+ php高级工程师需要掌握的知识点