1.1 动态编程(通过反射读写类&方法)

准备:配置文件+创建调用类


自定义的,类和方法用标识符分开就行

cn.edut.com.tarena.A   ;   a
cn.edut.com.tarena.B;b

调用类

package cn.edut.com.tarena;

public class A {
	
	//@Test(value = "测试", paraClass = { String.class , Integer.class})
	public void a() {
		System.out.println("好嗨喔");
	}
}

package cn.edut.com.tarena;

public class B {
	public void b() {
		System.out.println("好嗨嗨嗨嗨嗨喔!!!");
	}
}

配置

重点

  • 获得、解析URL
  • IO流获得URL信息
package cn.edut.com.tarena;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;

public class Runner {
	private static List<String> list = new ArrayList<>();

	static {
		try {
			/* * "/" --- Runner类文件存放的目录,即bin "/config.tst" --- bin目录/config.txt */
			URL url = Runner.class.getResource("/config.txt");
			String path = url.getPath();
			// "d:/中/bin/config.txt" ---URL编码---> "d:/%E4%B8%AD/bin/config.txt"
			// ---编码---> "d:/中/bin/config.txt"
			// decode 解码
			path = URLDecoder.decode(path, "utf-8");
			System.out.println(path);

			BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(path), "GBK"));
			String line;
			while (null != (line = in.readLine())) {
				// line = line.replace("\\s+", "");
				line = line.trim();
				list.add(line);
			}

			in.close();

			System.out.println("配置成功 ! ");
		} catch (Exception e) {
			System.out.println("配置失败...");
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {

	}
}

反射实现方法调用

实现代码

package cn.edut.com.tarena;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;

import com.sun.xml.internal.ws.runtime.config.TubelineFeatureReader;

import sun.launcher.resources.launcher;

public class Runner {
	private static List<String> list = new ArrayList<>();

	static {
		try {
			/* * "/" --- Runner类文件存放的目录,即bin "/config.tst" * --- bin目录/config.txt */
			URL url = Runner.class.getResource("/config.txt");
			String path = url.getPath();
			// "d:/中/bin/config.txt" ---URL编码---> "d:/%E4%B8%AD/bin/config.txt"
			// ---编码---> "d:/中/bin/config.txt"
			// decode 解码
			path = URLDecoder.decode(path, "utf-8");
			System.out.println(path);

			BufferedReader in = new BufferedReader(
					new InputStreamReader(
							new FileInputStream(path), "GBK"));
			String line;
			while (null != (line = in.readLine())) {
				//注意:这里用replace,无法去除多个空格
				line = line.replaceAll("\\s+", "");
				list.add(line);
			}

			in.close();

			System.out.println("配置成功 ! ");
			
		} catch (Exception e) {
			System.out.println("配置失败...");
			e.printStackTrace();
		}
	}
	
	private static void launch() throws Exception {
		for(String l : list ) {
			String[] a = l.split(";");
			Class<?> clazz = Class.forName(a[0]);
			Method method = clazz.getDeclaredMethod(a[1]);
			method.setAccessible(true);
			method.invoke(clazz.newInstance()) ; 
		}
	}
	
	

	public static void main(String[] args) throws Exception {
		launch();
	}
}



1.2 注解

注解为其他开发工具,或其他java程序,来提供代码的额外信息

@Override

public String toString() {

   ...

}


  • Override注解,告诉编译器,这里实在重写一个方法,让编译器来检查,重写语法是否正确
  • 自定义注解,要自己编写处理代码

1.2.1 概念

注解很厉害哦,它可以增强我们的java代码,同时利用反射技术可以扩充实现很多功能。

<mark>它们被广泛应用于三大框架底层。</mark>
传统我们通过xml文本文件声明方式,
而现在最主流的开发都是基于注解方式,代码量少,
<mark>框架可以根据注解去自动生成很多代码</mark>,从而减少代码量,程序更易读。
例如最火爆的SpringBoot就完全基于注解技术实现。

注解设计非常精巧,初学时觉得很另类甚至多余,甚至垃圾。
有了java代码干嘛还要有@注解呢?
但熟练之后你会赞叹,它竟然可以超越java代码的功能,让java代码瞬间变得强大。

大家慢慢体会吧。

<mark>常见的元注解:@Target、@Retention,jdk提供将来描述我们自定义的注解的注解</mark>。
听起来好绕,别着急,做两个例子,立刻清晰。
现在现有“<mark>元注解</mark>”这个概念。




1.2.2 分类

  • JDK自带注解
  • <mark>元注解</mark>
  • 自定义注解



1.2.3 JDK注解

JDK注解的注解,就5个:

  • <mark>@Override</mark>
  • @Deprecated - 标记就表明这个方法已经过时了。 但我就要用,别提示我过期
  • @SuppressWarnings(“deprecation”) - 忽略警告
  • @SafeVarargs - jdk1.7出现,堆污染,不常用
  • @FunctionallInterface jdk1.8出现,<mark>配合函数式编程拉姆达表达式</mark>,不常用



1.2.4 <mstyle mathcolor="&#35;ffaa11"> </mstyle> \color{#ffaa11}{**元注解**}

描述注解的注解,就5个:

  • <mark>@Target</mark> 注解用在哪里:类上、方法上、属性上
  • <mark>@Retention</mark> 注解的声明周期:源文件中、class文件中、运行中
  • @Inherited 允许子注解继承
  • @Documented 生成javadoc时会包含注解,不常用
  • @Repeatable注解为可重复类型注解,可以在同一个地方多次使用,不常用





1.3 <mstyle mathcolor="&#35;ff9911"> </mstyle> \color{#ff9911}{**元注解**}

1.3.1 @Target

Target 目标

描述注解的使用范围:

  • ElementType.ANNOTATION_TYPE 应用于注释类型
  • ElementType.CONSTRUCTOR 应用于构造函数
  • <mark>ElementType.FIELD 应用于字段或属性</mark>
  • ElementType.LOCAL_VARIABLE 应用于局部变量
  • <mark>ElementType.METHOD 应用于方法级</mark>
  • ElementType.PACKAGE 应用于包声明
  • ElementType.PARAMETER 应用于方法的参数
  • <mark>ElementType.TYPE 应用于类的任何元素(也可以用于类上 - Spring就这么用)</mark>



1.3.2 @Retention

<mark>定义了该注解被保留的时间长短,</mark>

  • 某些注解仅出现在源代码中,而被编译器丢弃;
  • 而另一些却被编译在class文件中(编译在class文件中的注解可能会被虚拟机忽略)
  • 而另一些在class被装载时将被读取。

为何要分有没有呢?
没有时,反射就拿不到,从而就无法去识别处理。

  • SOURCE 在源文件中有效(即源文件保留)
  • CLASS 在class文件中有效(即class保留)
  • <mark>RUNTIME 在运行时有效(即运行时保留)</mark> <mstyle mathcolor="&#35;ff9911"> </mstyle> \color{#ff9911}{(通常用这个)}





1.4 自定义注解 - 例子

自定义Test注解

package cn.edut.com.tarena.test;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/* * 元注解:对注解的注解 * * @Target 设置注解目标:类、方法、成员变量、参数变量... * @Retention 保留范围:源码、字节码、运行期内存 * 源码:编译成字节码时被丢弃 * 字节码:类被加载到内存时丢弃 * 运行期内存:在内存中保留 */

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
	//如果没有默认值,使用注解时,就必须赋值
	//有默认值,可以不赋值
	int id() default 0 ;
	String title() default "" ;
	/* * 特殊属性名 value * 有特殊待遇,单独赋值时,可以不写value= * 可以作为一个有意义的属性名别名 * @Test("adfasf") * @Test(id=5, value="adfasf" ) * @Test(id=5, title="adfasf" ) */
	
	//title的别名
	String value() default "" ;
}

A类 - 用于测试

package cn.edut.com.tarena.test;

public class A {
	@Test(value="value.a",title="title.A.a",id=1)
	private void a() {
		System.out.println("A.a");
	}
	
	@Test("value.b")
	private void b() {
		System.out.println("A.b");
	}
	
	//@Test(title="title.C" , "value.c") 报错
	private void c() {
		System.out.println("A.c");
	}
	
	@Test(title="title.d", id=4)
	private void d() {
		System.out.println("A.d");
	}
}

Runner - 扫描自定义注解

运行相应的方法

package cn.edut.com.tarena.test;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

public class Runner {
	//在指定的类中,自动发现自动运行有@Test注解的方法
	public static void main(String[] args) throws Exception {
		Runner.lunch(A.class);
	}
	
	private static <T> void lunch(Class<T> c) throws Exception {
		T obj = c.newInstance();
		Method[] methods = c.getDeclaredMethods();
		for (Method method : methods) {
			method.setAccessible(true);
			//在方法上,是否存在Test类型的注解
			if(method.isAnnotationPresent(Test.class)) {
				//获取 Test 注解数据
				Test t = method.getAnnotation(Test.class);
				System.out.println(t.id()+"-"+t.title()+"-"+t.value());
				method.invoke(obj);
				System.out.println("-----------------");
			}
		}
	}
}