JAVA潜心修炼五天——第5天

JAVA潜心修炼五天——第5天

反射

类加载

将class文件读取到内存中的过程,称为类加载

class文件的加载由ClassLoader完成,称为类加载器,类加载器有三种类型:

  • BootStrapClassLoader:启动类加载器,用C++编写的,由JVM在启动时加载初始化的,主要负责加载%JAVA_HOME%/jre/lib路径下的类

  • ExtClassLoader:扩展类加载器,由BootStrapClassLoader加载,主要负责加载%JAVA_HOME%/jre/lib/ext路径下的类

  • AppClassLoader:系统类加载器,由BootSreapClassLoader加载,主要负责加载classpath路径下的类(对Eclipse就是项目路径/bin目录)

    可以使用 类名.class.getClassLoader() 获取当前类的类加载器

class文件读取到内存中会被封装成java.lang.Class类的对象

反射

在程序运行状态中:

  • 对于任意一个类,都能够获取这个类的所有属性和方法
  • 对于任意一个对象,都能够调用这个对象的任意一个属性和方法

这种在运行时动态获取信息以及动态调用对象方法的功能称为反射reflection

相关API

反射相关的API都在java.lang包中

类/接口名作用
Class表示运行中的类和接口
Field表示类中的属性
Method表示类中的方法
Constructor表示类中的构造方法
Package表示类所属的包
Modifier表示修饰符
Parameter表示方法的参数
Annotation表示注解

Class对象

运行中的class文件通过Class对象来表示的

  • Class对象是在类加载时由JVM自动创建的,一个类在JVM中只会有一个Class对象
  • Class类没有公共的构造方法,不能自己创建Class对象,但可以获取其实例并进行操作

Class是反射的核心类,要想操作类中和属性和方法,都必须从获取Class对象开始

获取Class对象(三种)
  1. 调用对象的getClass()方法
  2. 调用类的class属性
  3. 调用Class类的forName()静态方法
常用方法
        Class cls= Student.class;
        //获取类中的信息
        System.out.println("类全名:"+cls.getName());
        System.out.println("简单类名:"+cls.getSimpleName());
        System.out.println("是否为接口:"+cls.isInterface());

        //获取父类
        Class superClass=cls.getSuperclass();
        System.out.println("父类:"+superClass.getSimpleName());

        //获取实现的接口
        Class[] interfaces=cls.getInterfaces();
        System.out.println("实现的接口:");
        for(Class c:interfaces){
            System.out.println(c.getSimpleName()+"\t");
        }
        System.out.println();

        //获取修饰符
        int modifiers=cls.getModifiers();
        System.out.println("修饰符: "+ Modifier.toString(modifiers));

        //调用无参构造方法创建对象
        try{
            Object obj=cls.newInstance();
            System.out.println(obj);
        }catch (InterruptedException e){
            System.out.println("实例化异常:"+e.getMessage());
        }catch (IllegalAccessException e){
            System.out.println("非法访问异常: "+e.getMessage());
        }

反射操作

操作Field
        Class cls=Student.class;
        //1.获取当前类及父类中所有public修饰的属性
        Field[] fields=cls.getFields();
        //2.获取当前类中所有的属性,包含private修饰的
        fields=cls.getDeclaredFields();
        for (Field f:fields){
            //属性名,属性类型,修饰符
            System.out.println(f.getName()+"\t"+f.getType()+"\t"+Modifier.toString(f.getModifiers()));
        }
        //3.获取当前类及父类中指定的public修饰的属性
        Field field=cls.getField("sex");

        //4.获取当前类中指定的属性
        field=cls.getDeclaredField("height");
        System.out.println(field);

        //通过反射为属性赋值
        Student stu=new Student();
        //取消权限控制检查,访问属性时忽略访问控制符(破坏了封装原则)
        field.setAccessible(true);
        //为stu对象的field熟悉赋值为180.5
        field.set(stu, 180.5);

        //通过反射获取属性值
        //获取stu对象的指定field属性
        Object value=field.get(stu);
        System.out.println(value);
操作Method
        Class cls=Student.class;

        //1.获取当前类及父类中所有额的public修饰的方法
        Method[] methods=cls.getMethods();

        //2.获取当前类中所有的方法,包含private修饰的
        methods=cls.getDeclaredMethods();

        for(Method m:methods){
            //方法名,返回值类型,修饰符,参数列表
            System.out.println(m.getName()+"\t"+m.getReturnType()+"\t"+Modifier.toString(m.getModifiers())+"\t"+ Arrays.toString(m.getParameters()));
        }

        //3.获取当前类及父类中指定的public修饰的方法
        Method method=cls.getMethod("c", int.class,int.class);

        //4.获取当前类中指定的方法
        method =cls.getDeclaredMethod("c", int.class,int.class);
        System.out.println(method);

        //通过反射调用方法
        Student stu=new Student();
        method.setAccessible(true);
        //调用stu对象的method方法,传入参数4和3.5
        Object value=method.invoke(stu, 4, 3.5);
        System.out.println(value);
操作Constructor
        Class cls=Student.class;

        //1.获取所有public修饰的构造方法
        Constructor[] constructors=cls.getConstructors();

        //2.获取所有的构造方法
        constructors=cls.getDeclaredConstructors();
        for(Constructor c:constructors){
            System.out.println(c);
        }
        //3.获取指定的public修饰的构造方法
        Constructor c=cls.getConstructor(String.class,String.class,double.class);
        //4.获取指定的构造方法
        c=cls.getDeclaredConstructor(String.class);
        System.out.println(c);
        //通过反射调用构造方法,创建对象
        c.setAccessible(true);
        Object obj=c.newInstance("male");
        System.out.println(obj);
        //直接调用Class对象的newInstance()方法创建对象
        Object obj2=cls.newInstance();
        System.out.println(obj2);

注解

Annotation注解是一种新的类型,与接口很相似,它与类,接口,枚举是在同一个层次

注解相当于是一种标记,以 @开头,一般写作 @xxx,可以声明在类,属性,方法,参数等的前面

用来对这些元素进行说明,标注或实现特定功能

JDK自带注解

在java.lang包中

注解作用
@Override标记在方法前面,表示该方法是重写父类的方法
@Deprecated标记在类,属性,方法等前面,表示该内容已过时,不建议使用
@SuppressWarning标记在类,属性,方法等前面,用来关闭某些警告信息
自定义注解

定义注解:使用 @interface 关键字+元注解(标注在注解上的注解,称为元注解)

语法:

//元注解
public @interface 注解名{//抽象方法}
元注解作用
@Target定义注解的作用域,即可以被标记的哪些元素上,通过枚举类型ElementType来取值,省略时表示可以声明在任何元素前
@Retention定义注解的生命周期,通过枚举类型RetentionPolicy来取值
@Documented指定当使用javadoc生成API文档时包含该注解信息
@Inheried指定允许子类继承父类中的注解
使用注解

语法 @注解名(属性名=属性值,属性名=属性值...)

  • 在使用注解时需要为注解中的抽象方法指定返回值,也称为:为注解指定属性值
  • 属性名就是注解中抽象方法的名称
注解解析

对于生命周期为运行期间(RetentionPolicy.RUNTIME)的注解,可以通过反射获取元素上的注解,实现特定的功能

注解的应用

通过注解加载属性文件,为类中属性赋值

public class User {
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Value{
            String value();
        }

    @Value("${user.username}")
    private String username;

    @Value("${user.pwd}")
    private String password;

    @Value("${user.email}")
    private String email;

    private String address;

    public String getUsername(){
        return username;
    }
    public User(){
        init();
    }

    //初始化
    private void init(){
        //加载属性文件
        Properties p=new Properties();
        try{
            p.load(User.class.getClassLoader().getResourceAsStream("info.properties"));
        }catch (IOException e){
            e.printStackTrace();
        }
        //获取类中所有属性上的@value注解,并解析处理
        //获取所有属性
        Class<? extends  User> cls=this.getClass();
        Field[] fields=cls.getDeclaredFields();
        //循环获取每个属性上的@Value注解
        for(Field field:fields){
            Value annotation=field.getAnnotation(Value.class);
            //判断是否有该注解
            if(annotation!=null){
                //获取注解上的值,并对值进行判断处理
                String value=annotation.value();
                if(value.startsWith("#{")&&value.endsWith("}")){
                    String key=value.substring(2,value.length()-1);
                    value=p.getProperty(key);
                }
                //为类中的属性赋值
                try{
                    field.setAccessible(true);
                    field.set(this,value);
                }catch (IllegalArgumentException e){
                    e.printStackTrace();
                }catch (IllegalAccessException e){
                    e.printStackTrace();
                }
            }
        }
    }
}

info.properties

user.username=admin
user.pwd=4566
user,email=tom@sina.com

设计模式

设计模式(Design Pattern)是一套被人反复使用,多数人知晓的,经过分类的,代码设计经验的总结。

使用设计模式的目的:为了代码可重用性,让代码更容易被他人理解,保证代码可靠性。

设计原则

面向对象的基本原则:

  • 高内聚——模块内部要高度内聚,每个类完成特定的独立的功能
  • 低耦合——模块之间要降低耦合度,类之间的依赖应该要尽量低
设计模式的六大原则
  1. 单一职责原则

    ​ 一个类只负责一个职责

  2. 开闭原则

    ​ 对扩展开放,对修改关闭

    ​ 在程序需要进行扩展的时候,不能去修改原有的代码,实现一个热拔插的效果(需要使用接口和抽象类)

  3. 里氏替换原则

    ​ 所有引用基类(父类)的地方必须能透明的使用其子类的对象。

    ​ 任何基类出现的地方,都可以换成子类,程序还可以正常执行

  4. 依赖倒置原则

    ​ 细节应当依赖于抽象,抽象不应该依赖于细节,也就是“面向接口编程”或“面向抽象类编程”

  5. 接口隔离原则

    使用多个隔离的接口,而不使用单一的总接口

    每个接口应该承担一个相对独立的角色,不干不该干的事

  6. 迪米特法则,也称为最少知道原则

    一个类对自己依赖的类知道的越少越好

23种设计模式

分为三大类:

  • 创建型模式:工厂模式,抽象工厂模式,单例模式,建造者模式,原型模式
  • 结构型模式:配适器模式,装饰器模式,代理模式,外观模式,桥接模式,组合模式,享元模式
  • 行为型模式:策略模式,模板模式,观察者模式,迭代子模式,责任链模式,命令模式,备忘录模式,状态模式,访问者模式,中介者模式,解释器模式

单例模式

Singleton单例模式 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

实现模式(两种)
饿汉式:在类加载时创建实例,不管后期是否会使用都会创建(线程安全)
懒汉式:在需要时才创建实例,延迟加载(线程不安全)

模板模式

Template定义一个抽象类,将部分逻辑以具体方法的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。

不同的子类可以不同的方式来实现这些抽象方法,从而实现对剩余的逻辑有不同的实现。

先制定一个顶级逻辑框架,而将逻辑的细节留给具体的子类来实现。

实现方式:
/**
 * 排序工具类,抽象类
 */
public abstract class SortUtil<T> {
    //将部分逻辑以具体的形式实现
    public void sort(T[] arr){
        //先制定一个顶级逻辑框架
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr.length-i-1; j++) {
                //将逻辑的细节留给具体的子类来实现
                int m=compareTo(arr[j],arr[j+1]);
                if(m>0){
                    T temp=arr[j];
                    arr[j]=arr[j+1];
                    arr[j+1]=temp;
                }
            }
        }
    }
    //声明一些抽象方法来迫使子类实现剩余的逻辑
    public abstract int compareTo(T o1,T o2);
}

修改后的模板模式

/**
 * 排序工具类
 */
public class SortUtil{

    //将部分逻辑以具体的形式实现
    public static <T> void sort(T[] arr,Comparable<T> t){
        //先制定一个顶级逻辑框架
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr.length-i-1; j++) {
                //将逻辑的细节留给具体的子类来实现
                int m=compareTo(arr[j],arr[j+1]);
                if(m>0){
                    T temp=arr[j];
                    arr[j]=arr[j+1];
                    arr[j+1]=temp;
                }
            }
        }
    }
}
public interface Comparator<T>{
    public abstract int compareTo(T o1,T o2);
}

工厂模式

Factory定义一个工厂类,对实现同一个接口的一组类进行实例化对象的操作

实现方式
/**
 * 工厂类
 */
public class MotoVehicleFactory {
    public MotoVehicle create(String type){
        if("car".equals(type)){
            return new Car();
        }else if("bus".equals(type)){
            return new Bus();
        }else if("truck".equals(type)){
            return new Truck();
        }
        return null;
    }
}

修改后的工厂模式

/**
 * 创建对象的工厂类
 *
 * 根据类路径下的objs.properties属性文件中的配置来创建对象
 * 要求:
 * 1.文件中不能有空行
 * 2.注意编写顺序,由于对象间可能存在依赖关系,被依赖的对象必须放在依赖对象的前面
 */
public class ObjectFactory {
    //定义一个Map集合,用来存储对象
    private static Map<String,Object> objs=new HashMap<>();

    //类加载时读取属性文件,创建相应对象,对象是单调
    static {
        try(BufferedReader reader=new BufferedReader(new InputStreamReader(ObjectFactory.class.getClassLoader().getResourceAsStream("objs.properties")));){
            String line=null;
            while ((line=reader.readLine())!=null){
                String[] entry=line.split("=");
                objs.put(entry[0],Class.forName(entry[1]).newInstance());
            }
        } catch (Exception e){
            throw new ExceptionInInitializerError("ObjectFactory初始化失败:"+e);
        }
    }
    //工厂方法,用来获取对象
    public static Object getObject(String key){
        return objs.get(key);
    }
}

代理模式

Proxy为其他对象提供一种代理,以控制对这个对象的访问,起到中介的作用。

被代理的对象称作目标对象,代替目标对象的对象称为代理对象,通过代理对象访问目标对象

作用:可以扩展目标对象的功能呢,增强额外的操作,同时不侵入原代码

代理的三要素:

  • 共同接口(被代理接口)

  • 目标对象(target)

  • 代理对象(proxy)

    实现方式(两种)
    静态代理

    代理类有程序员创建或工具生成

    所谓静态就是在程序运行前就已经存在代理类的class文件,代理类和委托类的关系在运行前就确定了

    **缺点:**代理对象需要和目标对象实现相同的接口,所以会有很多代理类,如果接口增加方法,目标对象和代理对象都要维护

    public class UserServiceProxy implements UserService {
        //private UserService userService=new UserServiceImpl();
        /*
        代理对象可以只声明目标对象的接口,不负责目标对象的创建,目标对象由外部创建并传入
         */
        private UserService userService=(UserService) ObjectFactory.getObject("userService");
    
        @Override
        public void login(String username,String password){
            System.out.println("login start: "+new Date().getTime());
            userService.login(username,password);
        }
    
        @Override
        public String logout(){
            System.out.println("logout start:"+new Date().getTime());
            return userService.logout();
        }
    }
    
    动态代理

    代理类是程序运行期间有JVM根据反射机制动态生成的,自动生成代理类和代理对象

    所谓动态是指在程序运行前不存在代理类的class文件,代理类和委托类的关系是在程序运行时确定

    动态代理的实现:

    • jdk技术:只适用于实现了接口的类,使用 java.lang.reflect.Proxy

    • cglib技术:可用于没有实现任何借口的类,需要使用第三方jar包(通过继承实现的,让代理类继承目标类)

      (PS:如果一个类没有实现任何接口,并且被final修饰,那么这个类无法创建代理对象)

    UserService userService=(UserService)Proxy.newProxyInstance(
    	//目标类的类加载器
    	UserServiceImpl.class.getClassLoader(),
    	//目标类的接口列表
    	new Class[]{UserService.class},
    	//代理要执行的操作
    	new InvocationHandler(){
    	//参数的含义:生成的代理对象,目标方法,方法参数
    	@Override
    	public Object invoke(Object proxyMethod method,Object[] args) throws Throwable{
    	System.out.println(method.getName()+"start:"+new Date().getTime()+",参数,"+Arrays.toString(args));
    	Object returnValue = method.invoke(new UserServiceImpl(),args);
    	return returnValue;
    	}
    	});
    
    userService.login("admin","123");
    System.out.println("---------------------------------");
    System.out.println(userService.logout());
    
    //代理类的类型 class com.sun.proxy.$Proxy0
    Syetem.out.println(userService.getClass());
    
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值