java反射基础 && 动态代理解决乱码问题 && 注解

前言: 每一天的坚持,就是成功的,加油吧!

java反射

类加载的概述

类加载分为三个步骤:类加载,,连接,,初始化

  1. 加载:指的是,将class文件读入到内存中,并为之创建一个Class对象。任何类被使用系统都会创建一个Class对象
  2. 连接:验证class文件知否符合要求。准备,为静态成员分配内存。解析class文件
  3. 初始化:进行构造方法的初始化

类加载的时机:

创建类的实例对象
调用类的静态成员,为静态变量赋值
调用类的静态方法。
使用反射生成相应的Class对象
初始化某个类的子类
直接运行主类

类加载器的分类

  • 根类加载器

BootstrapClassLoader
负责一些核心类的加载,比如String int float System
这些类频繁的与内存进行交互,一般是非常快,是伪类,用c c++写

  • 扩展类加载器

Extension ClassLoader 在ext包下的jar都会该加载器

  • 应用类加载器

App ClassLoader 平常我们自定义的类都是用的应用类的加载器

反射概述

反射就是获得某个类在运行状态中的字节码对象,Class对象,并根据该对象中的方法,去解析该类中的属性和方法。并且去调用该类的属性和方法。
这种动态获取信息,并且动态调用对象的方法就是java语言的反射机制

获取Class对象

  1. 类名.class
  2. new 类名().getClass()
  3. Class,forName(“类的全路径”)

反射的使用

基本用法

  • 通过反射获取无参的构造方法和获取带参的构造方法并使用
 public static void main(String[] args) throws Exception{
        Class<People> clazz = People.class;
        //获得该Class对象所表示的类的无参的构造方法,并为其创建一个实例.
        //不是无参的构造,就不能用这种方式
        People people = clazz.newInstance();
        String b = people.b;
        System.out.println(b);

        //获取该Class对象所表示的类的有参的构造方法,参数写相对应参数类型的Class对象,不写默认为空,返回无参构造方法
        Constructor<People> constructor = clazz.getConstructor(String.class);
        People people = constructor.newInstance();
        String b = people.b;
        System.out.println(b);
    }
  • 通过反射获取公共的成员变量和获取私有的成员变量
public static void main(String[] args) throws Exception {
		//先通过反射获取实例
        Class<People> clazz = People.class;
        People people = clazz.newInstance();

        //获取该Class对象表示的类的公共的成员变量
        Field b = clazz.getField("b");
        String o = (String)b.get(people);
        System.out.println(o);

        //获取Class对象表示的类的私有的成员变量
        //通过暴力反射
        Field a = clazz.getDeclaredField("a");
        //暴力反射要先设置权限
        a.setAccessible(true);
        int o1 = (int)a.get(people);
        System.out.println(o1);
    }
  • 通过反射获取公共的成员方法

Class对象中的getMethod() 方法,获取该方法,返回Method对象
Method对象中的invoke() 方法,执行该方法

这是在运行期间,通过反射获取字节码对象,并且运行方法的方式
public static void main(String[] args) throws Exception {
        Class<People> clazz = People.class;
        Constructor<People> constructor = clazz.getConstructor(String.class);
        People people = constructor.newInstance("dsa");

        //获取获取该Class对象表示的类的公共的成员方法
        Method m = clazz.getMethod("text");
        Object invoke = m.invoke(people);
        System.out.println(invoke);
    }
  • 通过反射越过泛型检查

泛型只在编译期间有效,在运行期间无效

通过反射实现动态代理

代理,相当于中介,自己要做的事情没有做,交给了别人

动态代理:程序运行的过程中产生的这个对象。
这个对象就是通过反射来获得的一个实例对象(代理对象),
  • 实现
    通过java.lang.reflect 包下的Proxy对象 和 InvocationHandler接口生成代理对象,注意,只能代理接口

    更强大的代理:使用cglib

    创建代理对象:

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
                               throws IllegalArgumentException
      

都会调用方法 InvocationHandler 接口中的 invoke() 方法

Object invoke(Object proxy,
              Method method,
              Object[] args)
              throws Throwable
  • 代码解析
 public static void main(String[] args) {
        /**
         * 第一个参数:告诉虚拟机通过反射创建出类的字节码文件用哪个字节码加载器(应用类加载器)加载到内存
         * 第二个参数:告诉虚拟机通过反射创建出来的字节码文件中应该有多少个方法,也就是被代理对象实现了那些接口
         * 第三个参数:告诉虚拟机创建的字节码文件中的方法应该怎么处理
         */
        Icar o =(Icar) Proxy.newProxyInstance(Demo1.class.getClassLoader(), GooleCar.class.getInterfaces(), new InvocationHandler() {
            /**
             *
             * @param proxy 动态代理的实例,不用管
             * @param method 动态代理对象正在执行的方法
             * @param args 动态代理对象正在执行的方法中的参数的值,不写默认为null
             * @return
             * @throws Throwable
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                String start = method.getName();
                if("start".contentEquals(start)){
                    /**
                     * 执行当前的方法。
                     * 第一个参数:是被代理的对象
                     * 第二个参数:方法执行的参数的值,不写默认为空
                     */

                    method.invoke(new GooleCar(),args);
                    System.out.println("hahahahahhah");
                }
                return null;
            }
        });
        o.start();
    }

动态代理解决乱码问题

当进行二次开发的时候,我们只有api,没有源码,也不能继承,要想增加功能,进行二次开发,使用动态代理模式

解决乱码问题,使用过滤器。每次请求都进行了中文乱码问题的处理。
实现:增请HttpServletRequest类中的getParameter()方法,使其获取中文数据的时候,不出现乱码问题

代码实现

final HttpServletRequest request = (HttpServletRequest) servletRequest;
要使用final修饰才可以
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        final HttpServletRequest request = (HttpServletRequest) servletRequest;
        //在不知道源码,不能继承的情况下改变request中的getParameter方法,解决乱码问题
        HttpServletRequest myRequest = (HttpServletRequest)Proxy.newProxyInstance(MyFilter.class.getClassLoader(), request.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object obj = null;
                String name = method.getName();
                if ("getParameter".equalsIgnoreCase(name)) {
                    //增强getParameter()方法
                    String method1 = request.getMethod();
                    if ("get".equalsIgnoreCase(method1)) {
                        //get请求解决乱码问题,转码
                        String invoke = (String) method.invoke(request, args);
                        String a = new String(invoke.getBytes("ISO-8859-1"), "utf-8");
                        return a;
                    } else { //post请求解决乱码问题
                        request.setCharacterEncoding("utf-8");
                         obj = method.invoke(request, args);
                    }
                }else {
                    obj = method.invoke(request,args);
                }
                return obj;
            }
        });
        filterChain.doFilter(myRequest,servletResponse);
    }

注解

强制转化遵循的规则

注解要求:

了解
开发中地位:类似dom4j解析XML文件. XML文件的解析程序员不会去解析,配置XML文件。 后期的开发中不会自定义注解,反射读取注解信息.

什么是注解

注解就相当于一个数据类型,与类、接口类似

注解的作用

  1. 配置文件
  2. 生成帮助文档
  3. 编译检查

注解特点

  • 直接可以在变量、方法、类之上加载
  • 注解可以由属性,也可以没属性
  • 注解的作用范围
  1. 源码期间有效

String类上的 ,注释里面的注解,帮助生成帮助文档,可以识别String类上的相关的注解
@Author, 表示作者
@Since,表示版本
@See ,参照的东西

  1. 编译期间有效

告诉编译器部分信息
@Override 声明当前方法是重写父类的方法
@Deprecated 声明以下方法是过时的方法,不建议大家使用
@Suppresswarning 抑制编译器发生警告信息

  1. 运行期间有效

当我们在当前代码上以Junit方式运行时,Junit会运行方法上包含@Test注解的方法
@Test
我们再当前代码上以Junit方式运行时,Junit会运行方法上包含@Test注解的方法

自定义注解

  • 格式:
public @interface 注解名称{
     public  属性类型  属性名称1();
     public  属性类型  属性名称2()  default  默认值;
  }
  • 自定义注解属性支持的类型:

基本数据类型(4类8种),String,Class,Annotation(注解类型),枚举类型,
以及以上类型的一维数组类型

  • 注解作用: 配置作用

配置:开发的时候部分信息不希望写死在程序中,例如数据库的用户名和密码,可以将用户名和密码存放在.txt , .properties ,
.xml文件中,利用程序来读取文件中的内容

框架:一大堆工具类组合,目的:加速项目开发

  • 什么时候用注解来做配置?

如果配置信息不会发生的修改,例如servlet路径,建议使用注解的形式
如果配置信息需要发生频繁的修改,例如数据库的用户名和密码信息, 建议采用传统方法 (.txt , .properties , .xml)

模拟Junit

通过反射读取字节码上的注解信息

md.isAnnotationPresent(MyTest.class)
  • 元注解

注解的注解,
声明当前注解作用域以及目标对象,如果没有声明,在运行期间是无法获取到注解的信息
@Runtention 声明当前注解的作用域
@target 声明当前注解的目标对象

  • 代码实现
  1. 自定义注解@MyTest
//自定义注解,相当于JUnit@Test

//定义注解的时候,需要通过元注解Retention说明当前自定义注解的作用域(Class,Source,Runtime)
@Retention(RetentionPolicy.RUNTIME)
//定义注解的时候,需要通过元注解Target说明当前的自定义注解的目标对象
@Target(ElementType.METHOD)
public @interface MyTest {
	//在MyTest注解中定义成员属性,默认值为-1
	public long timeout() default -1;
}

  1. 定义UserDao

public class UserDao {
	
	static{
		System.out.println("加载静态代码段的消息");
	}
	
	@MyTest
	public void addUser(){
		System.out.println("增加用户");
	}
	@MyTest
	public void delUser(){
		System.out.println("删除用户");
	}
	@MyTest
	public void uptUser(){
		System.out.println("更新用户");
	}
	public void getUser(){
		System.out.println("获取用户");
	}
}

  1. 定义类MyJunit ,模拟JUnit

public class MyJunit {
	public static void main(String[] args) throws Exception {
		//加载UserDao.class字节码文件中的方法,判断哪些方法上有自定义的注解@MyTest,如果当前的方法有@MyTest,执行,否则不执行
		
		//1_将UserDao.class字节码文件加载到内存中 ,class对象(代表字节码文件在内存中的对象)
		Class clazz=Class.forName("com.itcast.test02.UserDao");
		
		Class claszz01=UserDao.class;
		Class claszz02=new UserDao().getClass();
		
		//2_获取字节码对象上所有的方法,返回Method数组对象,数组中的每一个元素都代表Method对象(相当于字节码上的每一个方法)
		Method[] mds = clazz.getMethods();
		//3_遍历字节码对象上所有的方法
		for (Method md : mds) {
			//测试方法的名称
			//System.out.println(md.getName());
			//判断当前方法上是否有@MyTest注解信息
			//System.out.println(md.isAnnotationPresent(MyTest.class));
			if(md.isAnnotationPresent(MyTest.class)){
				//如果当前的方法上有@MyTest注解,执行,否则忽略
				md.invoke(new UserDao());
			}
			
		}
	}
}

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java是一种面向对象的编程语言,由Sun Microsystems公司(现在是Oracle公司)于1995年首次发布。Java语言的设计目标是写一次,到处运行(Write Once, Run Anywhere),即一次编写的程序可以在不同的计算机平台上运行,而无需重新编写或修改。 Java是一种高级语言,它提供了许多功能和类库,使得开发者能够更容易地编写复杂的应用程序。它使用了一种自动内存管理机制,即垃圾回收,来帮助程序员管理内存。这样一来,开发者不再需要手动分配和释放内存,大大减少了内存泄漏等问题的可能性。 Java语言具有平台无关性,这意味着一旦一个Java程序被编译为字节码,它可以在任何支持Java虚拟机(JVM)的计算机上运行,而不管该计算机的硬件和操作系统是什么。这给程序开发和部署带来了便利性和灵活性。 Java语言还以其安全性和可靠性而闻名。Java提供了一些内置的安全功能,如类加载和访问控制机制,可以确保只有授权的代码才能被执行。此外,Java的强类型检查机制可以防止类型相关的错误。 当今,Java已经成为世界上最流行的编程语言之一。它被广泛应用于企业级软件开发、移动应用开发、云计算、游戏开发等各个领域。Java的广泛应用和强大的生态系统也为开发者提供了许多工具和框架来简化开发工作。 总之,Java语言具有平台无关性、安全性、可靠性和功能丰富的特点,使其成为一种广泛应用的高级编程语言。无论是对专业开发人员还是对初学者来说,学习和掌握Java都是非常有价值的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值