Java---注解、类加载器-加强-实现运行任意目录下class中加了@MyTest的空参方法

简介: Java---注解、类加载器-加强-实现运行任意目录下class中加了@MyTest的空参方法

做自己的类加载器


虚拟机的核心是通过类加载器来加载.class文件,然后进行相应的解析执行。那么我们可以自己做类加载器,手动加载需要的.class以进行解析执行,从而扩展虚拟机的功能。


以下内容摘自API文档:


应用程序需要实现 ClassLoader 的子类,以扩展 Java 虚拟机动态加载类的方式。


网络类加载器子类必须定义方法 findClass 和 loadClassData,以实现从网络加载类。下载组成该类的字节后,它应该使用方法 defineClass 来创建类实例。


代码示例:

自己的类加载器 MyClassLoader

package cn.hncu;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import org.junit.Test;
public class MyClassLoader extends ClassLoader{
    public Class<?> findClass(String name){
        //name = "e:\\cn\\hncu\\Person.class"
        Class c = null;
        FileInputStream in;
        byte[] b=null;
        //通过IO或网络把字节码数据读取到buf[]当中。进一步地,
        //如果我们自己熟悉字节码的生成格式,那么也可自己用程序生成。
        //本例,我们是把硬盘中的一个外部字节码文件的数据读取到buf[]当中
        //1
        try {
            in = new FileInputStream(name);
            byte[] buf = new byte[1024]; 
            ByteArrayOutputStream baos = new ByteArrayOutputStream();//字节流
            int len=0;
            while((len=in.read(buf))!=-1){
                baos.write(buf, 0, len);
            }
            in.close();
            baos.close();
            b = baos.toByteArray();
        //2 ---1-2这里可以抽取出来写一个loadClassData方法
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        c = defineClass("cn.hncu.Person", b, 0, b.length);
        return c;
    }
    @Test
    public void testClassData() throws ReflectiveOperationException{
        String className="cn.hncu.Person";
        //用Java的类加载器加载一个
        Class c = Class.forName(className);
        Object obj = c.newInstance();
        System.out.println(obj);
        System.out.println((Person)obj);
        System.out.println("-------------------");
        className = "e:\\cn\\hncu\\Person.class";
        Class c2 = findClass(className);
        Object obj2 = c2.newInstance();
        System.out.println(obj2);
        System.out.println((Person)obj2);//这句是有问题的
        //※不同类加载器加载的对象是无法强转---可以理解是不同的生存空间
        //Person p2 = (Person) obj2;//会挂的。
        //因为obj2的生存空间是MyClassLoader,而Person的生成空间是AppClassLoader
        //System.out.println(p2);
    }
}


测试结果:

image.png

看,最后那句不能输出吧。

因为不是一个类加载器的。


作自己的测试工具MyJUnit

(注解与反射共同使用的案例 )


相关说明:


1)JUnit用的是@Test注解,我们用@MyTest注解。


2)JUnit已经嵌入到MyEclipse当中,我们自己的MyJUnit只要能独立运行就可以(不嵌入),同时这样我们也不方便在MyJUnit中以参数方式接收到被测试类的类名与方法名,只能以键盘输入的方式接收。


3)JUnit能实现指定单个方法来调用执行,由于不能利用MyEclipse传参,因此我们在MyJUnit程序中遍历所有的方法并通过判断是否声明@MyTest注解来决定是否调用执行该方法。


下面实现了运行任意目录下的实现了@MyTest注解的方法:

需要输入绝对路径名和类的完整名字。


注解:@MyTest


package cn.hncu.myJunit;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)//运行时也存在,必须要加这个
@Target (ElementType.METHOD)//限制注解只能加在方法上
public @interface MyTest {
}


测试类:TestPerson

package cn.hncu.myJunit;
/**
 * 测试用的
 * @author 陈浩翔
 *
 * @version 1.0  2016-5-6
 */
public class TestPerson {
    public void run1(){
        System.out.println("run1...");
    }
    @MyTest
    public void run2(){
        System.out.println("run2...");
    }
    public void run3(){
        System.out.println("run3...");
    }
    @MyTest
    public void run4(){
        System.out.println("run4...");
    }
    public void run5(){
        System.out.println("run5...");
    }
}


MyClassLoader类:自己写的类加载器

package cn.hncu.myJunit;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
 * 自己写的类加载器
 * @author 陈浩翔
 *
 * @version 1.0  2016-5-6
 */
public class MyClassLoader extends ClassLoader{
//我把它分成2个方法写了。
    public  Class<?> findClass(String name, String className) {
        try {
            byte b[] = loadClassData(name);
            Class c = defineClass(className, b, 0, b.length);
            return c;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
    private static byte[] loadClassData(String name) throws IOException {
        byte buf[] = new byte[1024];
        FileInputStream in = new FileInputStream(name);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int len=0;
        while((len=in.read(buf))!=-1){
            out.write(buf, 0, len);
        }
        in.close();
        out.close();
        byte b[] = out.toByteArray();
        return b;
    }
}


main方法类:

package cn.hncu.myJunit;
import java.lang.reflect.Method;
import java.util.Scanner;
import cn.hncu.myJunit.MyClassLoader;
/**
 * @author 陈浩翔
 * @version 1.0  2016-5-6
 */
public class MyJunit {
    public static void main(String[] args) throws ReflectiveOperationException {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入需要运行的类的绝对路径(路径中不能有空格,需要类的.class文件):");
        String name = sc.next();
        System.out.println("请输入类的名称(包含包名):");
        String className = sc.next();
        Class c = (new MyClassLoader()).findClass(name, className);
        //获得那个类了。
        //那个类必须要有空参构造方法
        Object obj = c.newInstance();
        //获得这个类所有声明的方法,包括私有的
        Method ms[] = c.getDeclaredMethods();
        for(Method m:ms){
            if(m.isAnnotationPresent(MyTest.class)){
                m.invoke(obj, null);
            }
        }
    }
}

运行测试结果:

image.png

现在我把class文件移动到D盘了。

image.png

再看运行结果:

image.png

这个可以有很多改进的地方,就比如每次输入路径都很麻烦,

我们可以做一个图形界面,让我们自己选择。

这样就方便多了。

目录
相关文章
|
2月前
|
消息中间件 Java Kafka
在Java中实现分布式事务的常用框架和方法
总之,选择合适的分布式事务框架和方法需要综合考虑业务需求、性能、复杂度等因素。不同的框架和方法都有其特点和适用场景,需要根据具体情况进行评估和选择。同时,随着技术的不断发展,分布式事务的解决方案也在不断更新和完善,以更好地满足业务的需求。你还可以进一步深入研究和了解这些框架和方法,以便在实际应用中更好地实现分布式事务管理。
|
2月前
|
安全 Java 开发者
Java中WAIT和NOTIFY方法必须在同步块中调用的原因
在Java多线程编程中,`wait()`和`notify()`方法是实现线程间协作的关键。这两个方法必须在同步块或同步方法中调用,这一要求背后有着深刻的原因。本文将深入探讨为什么`wait()`和`notify()`方法必须在同步块中调用,以及这一机制如何确保线程安全和避免死锁。
47 4
|
2月前
|
Java
深入探讨Java中的中断机制:INTERRUPTED和ISINTERRUPTED方法详解
在Java多线程编程中,中断机制是协调线程行为的重要手段。了解和正确使用中断机制对于编写高效、可靠的并发程序至关重要。本文将深入探讨Java中的`Thread.interrupted()`和`Thread.isInterrupted()`方法的区别及其应用场景。
55 4
|
2月前
|
Java 数据处理 数据安全/隐私保护
Java处理数据接口方法
Java处理数据接口方法
27 1
|
设计模式 Java
【玩转23种Java设计模式】学习目录汇总整理
软件设计模式(Design pattern),又称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。
【玩转23种Java设计模式】学习目录汇总整理
|
安全 Java
Java学习目录
基础 Java 比较器Comparator和Comparable的使用和区别 进制、移位 Java 移位运算详解(2进制+原码+反码+补码) Thread线程 Java Thread线程使用、线程安全(一) ...
872 0
|
10天前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者
|
12天前
|
安全 Java Kotlin
Java多线程——synchronized、volatile 保障可见性
Java多线程中,`synchronized` 和 `volatile` 关键字用于保障可见性。`synchronized` 保证原子性、可见性和有序性,通过锁机制确保线程安全;`volatile` 仅保证可见性和有序性,不保证原子性。代码示例展示了如何使用 `synchronized` 和 `volatile` 解决主线程无法感知子线程修改共享变量的问题。总结:`volatile` 确保不同线程对共享变量操作的可见性,使一个线程修改后,其他线程能立即看到最新值。
|
12天前
|
消息中间件 缓存 安全
Java多线程是什么
Java多线程简介:本文介绍了Java中常见的线程池类型,包括`newCachedThreadPool`(适用于短期异步任务)、`newFixedThreadPool`(适用于固定数量的长期任务)、`newScheduledThreadPool`(支持定时和周期性任务)以及`newSingleThreadExecutor`(保证任务顺序执行)。同时,文章还讲解了Java中的锁机制,如`synchronized`关键字、CAS操作及其实现方式,并详细描述了可重入锁`ReentrantLock`和读写锁`ReadWriteLock`的工作原理与应用场景。
|
13天前
|
安全 Java 编译器
深入理解Java中synchronized三种使用方式:助您写出线程安全的代码
`synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。
35 3