本节内容:

  • 类加载器
  • 反射
  • 注解 @xxx
  • 动态代理

 

一、类加载器

1. 什么是类加载器,作用是什么?

类加载器(ClassLoader)就是加载字节码文件(.class)。站在累加载器的角度上看CodeSegment,就是一个一个Class对象。

类加载器、反射、注解和动态代理

 

类加载器、反射、注解和动态代理

站在ClassLoader角度来讲,每个class就是一个class对象。而在每个class类对象里,又有属性、方法。

类加载器、反射、注解和动态代理

 

类加载器、反射、注解和动态代理

 

2. ClassLoader的类加载机制

并非一次性加载,需要的时候加载(运行期间动态加载)。不是从头到尾扫描,有多少类就加载进多少类,而是用到的时候才会去加载。

【示例】:TestDynamicLoading.java

package com.itheima.reflection;

public class TestDynamicLoading {

	public static void main(String[] args) {
		new A();
		System.out.println("=========");
		new B();
		
		new C();
		new C();
		
		new D();
		new D();
	}

}

class A {
	
}

class B {
	
}

class C {
	static {
		System.out.println("cccccccc");
	}
}

class D {
	{
		System.out.println("dddddd");
	}
}

如果A.class和B.class同时加载进来,那么“======”会在它们加载完成后打印出来;

如果是看到A加载A,看到B才加载B,那么“======”会在它们中间。

下面设置下运行配置,鼠标右击选择“Run As”,“Run Configuration”,(x)= Arguments,在“VM arguments”里输入:

-verbose:class

类加载器、反射、注解和动态代理

类加载器、反射、注解和动态代理

所以这叫做动态加载。

另外,注意看类C中的static语句块,static语句块中的内容会在类加载进内存后被调用一次,注意无论你生成多少个C的对象,只会调用一次。  

而类D中的代码叫动态语句块,每次new出来一个对象的时候就会去执行动态语句块中的内容。动态语句块用的不多。


3. 类加载器的种类

类加载器有三种,不同类加载器加载不同的字节码:

类加载器、反射、注解和动态代理

  • BootStrap:引导类加载器:加载都是最基础的文件。这是最核心的类加载器。
    • 这里的BootStrap和前端学习的BootStrap不是一个东西,前端学习的BootStrap是一个前端框架。这里的BootStrap底层是C语言写的,Java是个高级语言,它是操作不了硬件的。它主要加载的是JVM运行时的最基础的一些jar包。其他的类加载器都是Java写的。其他的类加载器要想工作的话,类加载器本身也是要加载到内存,下面的类加载器都是被BootStrap类加载器加载到内存中,然后其他的类加载器在加载其他的需要的class进内存。
  • ExtClassLoader:扩展类加载器:加载都是基础的文件,加载JVM的一些基础性jar包中的类。
  • AppClassLoader:应用类加载器:第三方jar包和自己编写java文件。
    • 第三方jar包和我们自己编写的jar文件是没区别的。我们自己写的java文件编译后也可以打成一个jar包。

【注意】:上面的关系不是继承,是加载器对象中有一个引用指向了上一层的加载器对象。 

 

怎么获得类加载器?(重点)

ClassLoader 字节码对象.getClassLoader();

获得类加载器之后,可以获得classes下任何资源:

package com.itheima.classloader;

public class Demo {
    public static void main(String[] args) {     
        //获得Demo字节码文件的类加载器
        Class clazz = Demo.class;//获得Demo的字节码对象
        ClassLoader classLoader = clazz.getClassLoader();//获得类加载器
        //getResource()中的的参数路径是相对于classes(src)
        //获得classes(src)下的任何的资源,这个jdbc.properties被放在了和Demo.java在一起
        String path = classLoader.getResource("com/itheima/classloader/jdbc.properties").getPath();
        //classLoader.getResourceAsStream("");
        System.out.println(path);
        
    }
}

 

二、反射

上面的字节码对象(Class对象)怎么获取?3种方式,上面的示例中只是这3种中第一种方式。

方式一,使用类的class属性:
Class<java.util.Date> clz1 = java.util.Date.class;
方式二,通过Class类中的静态方法forName(String className),传入类的全限定名(必须添加完整包名)。
Class<?> clz2 = Class.forName(“java.util.Date”); //把名字为java.util.Date的类加载进内存
方式三,通过对象的getClass方法来实现,其中,getClass()是Object类中的方法,所有的对象都可以调用该方法
java.util.Date str = new java.util.Date();
Class<?> clz3 = str.getClass();

反射是在运行期间动态地加载一个类进来,动态地new一个对象出来,动态地去了解这个对象的内部结构,动态地去调用这个对象的某一些方法。在学习框架时,很多的类的名字是写在配置文件中的。这时候你就会想它们是怎么new出来的,就是用反射机制new出来的。  

【示例】:建立一个properties文件,把需要动态加载的类名写进去

test.properties

com.itheima.reflection.T

接着写一个程序从properties中把这个类名读出来,然后生成它的一个对象。

TestFlection.java

package com.itheima.reflection;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class TestFlection {

	public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		String str = "com.itheima.reflection.T"; //必须是类全名,这块从properties文件中读取出来	
		Class c = Class.forName(str);
		Object o = c.newInstance();
		Method[] methods = c.getMethods(); //获取类里面有哪些方法,这些方法有哪些信息,放到method对象里
		for (Method m : methods) {
			//System.out.println(m.getName());
			if(m.getName().equals("mm")) {
				m.invoke(o); //m.invoke(obj, args) obj:方法属于哪个对象的,args:方法的参数。参数可以有多个,也可以不传,属于可变参数的方法
			}
			
			if(m.getName().equals("m1")) {
				for (Class paramType : m.getParameterTypes()) {
					System.out.println(paramType.getName());
				}
				m.invoke(o, 1, 2); 
			}
			
			if(m.getName().equals("getS")) {
				Class returnType = m.getReturnType();
				System.out.println(returnType);
				System.out.println(returnType.getName());
			}
		}
	}

}

class T {
	int i;
	String s;
	
	static { //可以写个static代码块看看是否被加载成功
		System.out.print("T loaded!");
	}
	
	public T() {
		System.out.println("T constructed!");
	}
	
	public void m1(int i, int j) {
		this.i = i + j;
		System.out.println(this.i);
	}
	
	public void mm() {
		System.out.println("m invoked");
	}
	
	public String getS() {
		return s;
	}
}

  

三、注解 @xxx

1. 什么是注解,注解作用

注解就是符合一定格式的语法 @xxxx。比如使用Junit是单元测试的工具,在一个类中使用 @Test 对程序中的方法进行测试。

注解是一种代码级别的说明。它是jdk1.5及以后版本引入的一个特性,与类、接口、枚举是同一个层次。

注解和注释区别:

  • 注释:在阅读程序时清楚 --给程序员看的
  • 注解:给jvm看的,给机器看的

注解在目前而言最主流的应用:代替配置文件。

  • 比如我们在用Eclipse创建动态web工程时,把"Dynamic web module version"选择2.5,这样创建完成后会有web.xml。如果选择3.0的版本,创建完成后会少个web.xml。因为注解可以代替webxml 

类加载器、反射、注解和动态代理

在src下新建一个Servlet

类加载器、反射、注解和动态代理

 

关于配置文件与注解开发的优缺点:

  • 注解优点:开发效率高、成本低
  • 注解缺点:耦合性大、并且不利于后期维护

 

企业中一般是注解和xml混用。基本不改的地方用注解,有可能要改的地方用xml配置文件。

 

2. jdk5提供的注解

  • @Override:JDK5表示告知编译器此方法是覆盖父类的,JDK6还可以表示实现接口的方法。
  • @Deprecated:表示被修饰的方法已经过时。过时的方法不建议使用,但仍可以使用。一般被标记为过时的方法都存在不同的缺陷:(1)安全问题;(2)新的API取代。
  • @SuppressWarnings:压制警告,被修饰的类或方法如果存在编译警告,将被编译器忽略。
    • deprecation:忽略过时
    • rawtypes:忽略类型安全
    • unused:忽略不使用
    • unchecked:忽略安全检查
    • null:忽略空指针
    • all:忽略所有。如果要忽略多个,可以直接all
import java.util.ArrayList;
import java.util.List;

/**
 * 测试JDK5提供的注解。
 * @author jkzhao
 *
 */
public class AnnoDemo {

    public static void main(String[] args) {
        //压制警告
        @SuppressWarnings({ "unused", "rawtypes" }) //如果里面要压制的太多,直接"all"
        List list = new ArrayList(); //这句话会报警告,因为没加泛型,集合中什么都可以存。取的时候自己不知道里面存的什么,可能存在转换错误的隐患。rawtypes
                                    //list未使用,也会出警告。unused

        show();

    }

    //定义方法过时
    @Deprecated
    public static void show(){

    }

    public static void show(String xx){

    }
 
    //帮助开发人间检查是否覆盖父类的方法正确。比如你把下面的方法改为public String toStringXX(),加上这个注解,就会报错了。帮你做了检查,你写错了。 
    //注解是给编译器用的,当你写错代码导致错误时,Eclipse会报红线,编译出错了。javac是编译,现在使用IDE工具,不需要自己手动编译,点击菜单栏的"Project",可以看到"Build Automatically"
    @Override
    public String toString() {
        return super.toString();
    }

}
AnnoDemo.java

相关文章:

  • 2021-10-09
  • 2022-12-23
  • 2018-06-05
  • 2022-12-23
  • 2019-01-23
  • 2021-11-03
  • 2021-07-27
猜你喜欢
  • 2022-12-23
  • 2021-09-02
  • 2018-10-08
  • 2021-10-28
  • 2022-01-24
  • 2022-12-23
相关资源
相似解决方案