注解
定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
作用分类:
①编写文档:通过代码里标识的注解生成文档【生成文档doc文档】
② 代码分析:通过代码里标识的注解对代码进行分析【使用反射】
③编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【Override】
- @Retention: 表示注解的生命周期
RetentionPolicy.SOURCE – 在编译阶段丢弃。这些注解在编译结束之后就不再有任何意义,所以它们不会写入字节码 RetentionPolicy.CLASS – 在类加载的时候丢弃。在字节码文件的处理中有用。注解默认使用这种方式。
RetentionPolicy.RUNTIME– 始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。
- @Target: 表示注解能够使用的位置(常用的位置有类、方法、字段、方法参数)
ElementType.TYPE
- 类型
ElementType.FIELD
- 字段
ElementType.METHOD
- 方法
ElementType.PARAMETER
- 参数
ElementType.CONSTRUCTOR
- 构造方法
ElementType.LOCAL_VARIABLE
- 局部变量
ElementType.ANNOTATION_TYPE
- 注解
ElementType.PACKAGE
- 包
- 使用注解完成包的扫描
package com.wz.anno;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//value属性是可以省略的
@Target(value = {ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)//生命周期
public @interface ScanPackage {
/**
* 要扫描的包,默认值为空,如果不给值,就默认扫描这个注解
* 所标记的类所在的包
* @return
*/
String value() default "";
}
package com.wz.anno;
import java.io.File;
import java.net.URL;
//使用注解
@ScanPackage("com")
public class AnnoTest {
public static void main(String[] args) {
Class<AnnoTest> clazz = AnnoTest.class;
//获取类上使用的ScanPackage注解
ScanPackage scanPackage = clazz.getAnnotation(ScanPackage.class);
if (scanPackage != null){
//获取要扫描的包
String pk = scanPackage.value();
if ("".equals(pk)){//如果要扫描的包为空字符串,说明没有给定扫描的包
//要扫描的包就是ScanPackage注解标记的类所在的包
pk = clazz.getPackage().getName();
//com.wz.anno
}
//包的本质就是文件夹,要将包变成文件夹,只需要将包名中的"."替换成"/"
String path = pk.replace(".","/");
//相对路径转换位可以定位的文件路径
//使用到类加载器来实现
ClassLoader classLoader = clazz.getClassLoader();
//利用类加载器获取文件的定位信息
URL url = classLoader.getResource(path);
if (url!=null){
File folder = new File(url.getPath());
//D:\Project%20file\fyOfflineCourse\MyDay10\out\production\MyDay10\com\wz\anno
scanFolder(folder,pk);
}
}
}
private static void scanFolder(File folder, String pk){
File[] files = folder.listFiles(file -> file.getName().endsWith(".class") || file.isDirectory());
if(files != null){
for (File file : files) {
if(file.isDirectory()){
scanFolder(file, pk + "." + file.getName());//递归扫描
} else {
String simpleName = file.getName();//类名
int index = simpleName.indexOf('.');
simpleName = simpleName.substring(0, index);
String className = pk + "." + simpleName;
System.out.println(className);
}
}
}
}
}
- 使用注解完成对象属性的赋值
package com.wz.anno03;
import java.lang.reflect.Field;
public class RejectionValueTest {
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
Class<Teacher> clazz = Teacher.class;
Teacher teacher = clazz.newInstance();
//获取字段
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
Value value = field.getAnnotation(Value.class);
if (value!=null){
String defaultValue = value.value();
if ("".equals(defaultValue)){
throw new RuntimeException("属性不能为空");
}
field.setAccessible(true);
//获取字段的类型
Class<?> fieldType = field.getType();
if(fieldType == String.class){
field.set(teacher, defaultValue);
} else if(fieldType.isPrimitive()){//如果字段类型是基本数据类型(包含了包装类)
if(fieldType == int.class || fieldType == Integer.class){
field.set(teacher, Integer.parseInt(defaultValue));
} else if(fieldType == short.class || fieldType == Short.class){
field.set(teacher, Short.parseShort(defaultValue));
} else if(fieldType == byte.class || fieldType == Byte.class){
field.set(teacher, Byte.parseByte(defaultValue));
} else if(fieldType == float.class || fieldType == Float.class){
field.set(teacher, Float.parseFloat(defaultValue));
} else if(fieldType == long.class || fieldType == Long.class){
field.set(teacher, Long.parseLong(defaultValue));
} else if(fieldType == double.class || fieldType == Double.class){
field.set(teacher, Double.parseDouble(defaultValue));
} else if(fieldType == boolean.class || fieldType == Boolean.class){
field.set(teacher, Boolean.parseBoolean(defaultValue));
} else if(fieldType == char.class || fieldType == Character.class){
field.set(teacher, defaultValue.charAt(0));
}
}
}
}
System.out.println(teacher);
}
}
package com.wz.anno03;
public class Teacher {
@Value("ZhangSan")
String name;
@Value("10000")
double salary;
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
", salary=" + salary +
'}';
}
}
package com.wz.anno03;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Value {
String value() default "";
}
ThreadLocal
1. 基本使用
ThreadLocal
是用来为当前线程维护的一个局部变量,这个维护的局部变量只有当前线程可以访问。ThreadLocal
底层采用的是ThreadLocalMap
来存储维护的数据。ThreadLocalMap
相当于一个Hashmap
,每一个线程自身都会维护一个ThreadLocalMap
对象(受包保护),因为这个ThreadLocalMap
中存储的数据就只有维护的线程才能访问。
ThreadLocal
的作用:
线程并发:在多线程环境下使用
传递数据:可以通过
ThreadLocal
在同一线程,不同组件中传递数据线程隔离:每个线程的变量都是独立的,不会相互影响
- 常用方法
方法声明 | 描述 |
ThreadLocal() | 创建ThreadLocal对象 |
public void set(T value) | 设置当前线程绑定的局部变量 |
public T get() | 获取当前线程绑定的局部变量 |
public void remove | 移除当前线程绑定的局部变量 |
ThreadLocal
基本使用
package com.wz.threadlocal01;
public class MyDemo {
private String content;
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
不使用
ThreadLocal
(无线程隔离)
package com.wz.threadlocal01;
public class ThreadLocalTest {
public static void main(String[] args) {
MyDemo demo = new MyDemo();
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
demo.setContent(Thread.currentThread().getName()+"的数据");
System.out.println("-----------------------");
System.out.println(Thread.currentThread().getName()+"---->"+demo.getContent());
}
});
thread.setName("线程"+i);
thread.start();
}
}
}
结果:
使用
ThreadLocal
(线程隔离)
package com.wz.threadlocal01;
public class MyDemo {
ThreadLocal<String> threadLocal = new ThreadLocal<>();
private String content;
public String getContent() {
return threadLocal.get();
}
public void setContent(String content) {
threadLocal.set(content);
}
}
在
MyDemo
类中,threadLocal
是一个ThreadLocal<String>
类型的成员变量。ThreadLocal
类是Java提供的一个特殊的类,可以创建线程局部变量。每个线程都可以独立地访问自己的线程局部变量,而不会与其他线程的线程局部变量产生冲突。
MyDemo
类还有一个私有的content
成员变量,它用于保存内容。
getContent()
方法用于获取线程局部变量的值,它通过调用threadLocal.get()
来实现。
setContent(String content)
方法用于设置线程局部变量的值,它通过调用threadLocal.set(content)
来实现。
结果:
2. TheadLocal
和synchronized
区别
- 使用
synchronized
package com.wz.threadlocal01;
public class ThreadLocalTest {
public static void main(String[] args) {
MyDemo demo = new MyDemo();
Object o = new Object();
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
synchronized (o){
demo.setContent(Thread.currentThread().getName()+"的数据");
System.out.println("-----------------------");
System.out.println(Thread.currentThread().getName()+"---->"+demo.getContent());
}
}
});
thread.setName("线程"+i);
thread.start();
}
}
}
结果:
使用
synchronized
虽然实现了线程隔离,但是牺牲了线程的并发执行的时间,以时间换空间
|
| |
原理 | 同步机制采用‘以时间换空间’的方式,只提供了一份变量,让不同线程排队访问 |
|
侧重点 | 多个线程之间访问资源的同步 | 多线程中让每个线程之间的数据相互隔离 |