一. 传参#
-
java普通类型如
int,double,char,与c++相同,都是值传递,方法内修改不影响外部传入的值
javapublic class ValuePassing { public static void main(String[] args) { int x = 10; modifyValue(x); // x仍然为10 } public static void modifyValue(int value) { value = 20; // 修改的是副本,不影响原始变量 } } -
java对象类型如
String,类似c++传递指针参数,传入的是指向对象的指针,可以在方法内修改对象,但对该对象整体重新赋值则相当于c++中让函数内指针指向新内存地址,不会改变外部对象
javapublic class Switch { public static void main(String[] args) { String x1 = "10"; String y1 = "20"; System.out.println("交换前x1 = "+x1); System.out.println("交换前y1 = "+y1); // 进行数据交换 swap(x1, y1); // x1, y1不会变化 System.out.println("交换后x1 = "+x1); System.out.println("交换后y1 = "+y1); } // 在c++中相当于swap(string* x, string* y) public static void swap(String x, String y) { // 跟c++类似,传递的指针实际是复制了一份指针指向对象 // java中重新赋值相当于c++让复制的那份指针指向别的内存,但原指针不变 String z ; z = x; x = y; y = z; System.out.println("x = "+x); System.out.println("y = "+y); } }
cvoid func() { string* s1 = new string("123"); string* s2 = new string("456"); swap(s1, s2); // s1, s2的指向仍然没变 } void swap(string* s1, string* s2) { string* temp = s1; s1 = s2; s2 = temp; } -
java引用传递,即传数组,与c++行为一致
二. 继承#
大致类似,主要区别:
- c++使用
: public ClassName表示继承,java使用extends ClassName表示继承 - c++可以多继承,java只有单继承
在可见性,继承规则上基本一致:
-
继承规则
-
默认继承父类的成员变量和方法
-
同名变量,使用时取决于静态类型
-
-
可见性
private:子类不可直接访问protect/public:子类可直接访问
2.1 方法重写#
java中子类定义与父类相同的方法(方法名,形参),默认是重写行为
c++中子类与父类相同的方法,默认是两个不同的方法
2.2 super#
作用:
-
在子类构造方法中调用父类的构造方法:
java// B继承A public B() { // 调用父类无参构造 super(); } public B(int x) { // 调用父类有参构造 super(x); } -
访问父类中的变量/方法
形式:
super.varsuper.method()
三. 多态#
3.1 强制方法重写#
java中在子类方法上使用@Override注解,可以由编译器检查是否进行了正确的重写
其行为与c++ override关键字类似,在子类方法后加上override,可以重写父类中定义的虚方法(virtual method)
java重写Object的三个方法(常用):
// 显示更有意义的字符串:
@Override
public String toString() {
return "";
}
// 比较当前对象和对象o是否相等: 逻辑相等
@Override
public boolean equals(Object o) {
return false
}
// 计算hash:
@Override
public int hashCode() {
return 0;
}java3.2 抽象类#
与c++中的纯虚类概念类似,包含抽象方法/纯虚函数的类
定义:
abstract class A {
private String s;
public abstract void method();
public void method2();
}java- 抽象类不能实例化
- 类和抽象方法都被
abstract修饰 - 可以定义普通变量和方法
3.3 类型转换#
-
子转父:直接强制类型转换即可
javaChild c = new Child(); Father f = (Father) c; // 去掉强制转换也生效 -
父转子:有风险,使用
instanceof
javaObject obj = "hello"; if (obj instanceof String) { String s = (String) obj; System.out.println(s.toUpperCase()); }
3.4 接口#
一组抽象方法的集合,所有方法默认是public abstract修饰
相当于c++的纯虚函数类只定义纯虚方法不定义变量和普通方法
-
定义:
javainterface A() { void method1(); } -
类实现接口:
javaclass B implements A { @Override public void method1() { } }
-
接口可以继承接口
-
类可以实现多个接口(
implements A, B) -
接口可以定义
default方法
javainterface A() { void method1(); default void method2() { method1(); System.out.println(""); } }default方法的目的是,当我们需要给接口新增一个方法时,会涉及到修改全部子类。如果新增的是default方法,那么子类就不必全部修改,只需要在需要覆写的地方去覆写新增方法。
当一个类实现了一个接口,就可以使用接口类型指向类的实例,从而调用实际的接口方法来完成对应功能
3.5 内部类#
顾名思义,就是在类A中定义类B,B被称为内部类
与c++不同,c++在类A中定义类B,除了影响类B的作用域外,两者几乎没有关系
java中三种内部类:
-
普通内部类,类A中明确定义类B:
javaclass Outer { private String name; Outer(String name) { this.name = name; } class Inner { void hello() { System.out.println("Hello, " + Outer.this.name); } } }- 内部类可以访问外部类的private字段
- 内部类依附外部类,持有一个外部类的引用
-
静态内部类(使用
static修饰内部类):
javaclass Outer { private static String NAME = "OUTER"; private String name; Outer(String name) { this.name = name; } static class StaticNested { void hello() { System.out.println("Hello, " + Outer.NAME); } } }- 不依附外部类
- 可以访问外部类的静态字段
-
匿名内部类(简化代码,临时使用)
java// 实现接口的匿名类 Runnable r = new Runnable() { @Override public void run() { System.out.println("Hello, " + Outer.this.name); } }; // 继承父类的匿名类 HashMap<String, String> map2 = new HashMap<>() { // 该类继承了HashMap, 可以访问其中的所有变量和方法 };- 继承父类或实现接口
- 可以访问父类中的变量和方法
3.5.1 lambda表达式#
java与c++中的lambda表达式形式基本相同,但本质不太一样,c++的lambda表达式本质是重载了()的对象,也可以叫做函数对象,java则是对匿名内部类的一种简化写法
-
java lambda:本质是实现接口的方法
实现条件:
- 接口
- 单一方法
或者用
@FunctionalInterface来注解一个接口
java@FuntionalInterface interface Swim { void swimming(); } public static void main(String[] args) { Swim swim = () -> { System.out.println("swim..."); }; }如上所示,当某个方法使用
Swim接口做参数时,可以这么写:
javavoid func1(Swim s) { ... } void call_func1() { func1(() -> { System.out.println("swim..."); }); }由于lambda表达式是对应接口的实现,所以
()是接口方法的形参列表,函数体返回值也要和接口方法保持一致 -
c++ lambda:
以
std::sort()为例:
cstd::vector<int> v {1, 2, 5, 4, 3}; std::sort(v.begin(), v.end(), [](int x, int y) -> bool { return x > y; });形式为:
c// -> return_type可省略 auto lambda = [捕获](形参列表) -> return_type { 函数体 };本质是把对象当函数用,没有java的必须实现接口的限制,还多了可捕获列表,不过二者在写法上大差不差
3.5.2 方法引用#
对lambda进一步的简化,用已有的方法来代替lambda表达式成为函数式接口的方法体,参数,返回值等要和接口方法保持一致
使用形式很像c++带作用域的函数:
class Function1 {
public static int subtraction(int a, int b) {
return b - a;
}
}
// 方法引用
Arrays.sort(arr, Function1::subtraction);java-
静态方法引用:
类名::方法名 -
对象方法引用:
对象::方法名- 引用本类方法:
this::方法名 - 引用父类方法:
super::方法名
- 引用本类方法:
-
类名引用成员:跟接口中抽象方法的第一个参数有关
javaArrayList<String> list = new ArrayList<>(); // list.stream.map(String::toUpperCase);如上,
map()里要求实现的抽象方法第一个参数是String str,那么可以直接引用String类的方法,其实际含义为调用str.toUpperCase() -
构造方法引用:
类名::new
四. 泛型#
类似c++模板,但是伪泛型(只在编译期检查)
-
泛型类
java// 可以指定多个类型 public class A<T, E> { } -
泛型方法
java// 在返回值前加<T>标识方法为泛型方法 public <T> void method(T val) { } -
泛型接口
javapublic interface B<E> { }实现类实现接口时,可以指定E为具体类型,也可以延续泛型,创建对象时再确定
泛型通配符
泛型不具备继承性,但数据具有继承性
? extends E:表示可以传递E和E的所有子类类型? super E:表示可以传递E和E的所有父类类型
五. 常用数据结构和API#
5.1 String#
-
构造
javapublic String(); public String(String original); public String(char[] chs); public String(byte[] chs); -
常用方法
javaint length(); // 长度 char charAt(int index); // 指定索引字符 char[] toCharArray(); // 转为字符数组 String substring(int beginIndex, int endIndex); // 截取子字符串 String toUpperCase(); // 大小写转换 boolean equals(Object anObject); // 比较 boolean equalsIgnoreCase(String antherString); // 忽略大小写比较 String[] split(String regex); // 根据指定正则表达式分割字符串
5.2 StringBuilder#
-
构造
javapublic StringBuilder(); public StringBuilder(String str); -
常用方法
javaStringBuilder append(任意类型); // 在末尾追加 StringBuilder reverse(); // 反转字符串 int length(); // 当前字符串长度 String toString(); // 转为String类型
5.3 StringJoiner#
添加字符串之间需要用间隔符分开,并最终需要添加开头和结尾符号
-
构造
javapublic StringJoiner(间隔符); public StringJoiner(间隔符, 开始符, 结尾符); -
常用方法
javaStringJoiner add(任意类型); // 追加 int length(); // 长度 String toString(); // 转为String类型
5.4 Math#
功能和cmath类似
使用时一般是Math.func()的形式:
int abs(int); // 绝对值
double ceil(double); // 上取整
double floor(double); // 下取整
int round(float); // 四舍五入
int max(int, int); // 最大
double pow(double, double); // 指数
double random(); // [0.0, 1.0)的随机值java5.5 System#
三个:退出,当前时间,数组拷贝
System.exit(0);
long ms = System.currentTimeMillis();
System.arraycopy(src, index1, dest, index2, len);java5.6 Object#
其中三个在强制方法重写里记过,toString(),equals(),hashCode()
-
clone()类似c++中的拷贝构造,同样分深克隆和浅克隆
-
浅克隆:重写的
clone()中直接调用super.clone(),会默认返回一个浅克隆的Object对象 -
深克隆:先
super.clone(),返回的对象中的引用数据类型重新从当前对象复制一遍即可
-
Objects:
三个静态方法,equals,isNull,nonNull
5.6 包装类和BigInteger#
-
就是封装一下原始的
int,double,boolean数据类型,使其变为类在保留了原数据类型的用法上,额外新增类的静态方法:
javaInteger valueOf(var); // 将var转为对应的类型 String toBinaryString(int); // 数字转二进制 String toOctalString(int); String toHexString(int); int parseInt(String); // 字符串转数字需要注意的是,包装类和
String一样是不变类,这意味着:
javaInteger i = 1000000; i += 1; // 会新生成一个Integer对象, i将指向这个新对象 -
BigInteger
每一个BigInteger都是一个不可变对象,换句话说每一次计算都会生成新的BigInteger对象
-
构造:
字符串:
new BigInteger("12345678")数字(最高支持long):
BigInteger.valueOf(1234) -
常用方法
javaBigInteger add(BigInteger val); BigInteger substract(BigInteger val); BigInteger multiply(BigInteger val); BigInteger divide(BigInteger val); BigInteger[] divideAndRemainder(BigInteger val); // 取余, 返回商和余数 BigInteger equals(BigInteger val); // 比较两个大整数是否相同 BigInteger pow(int val); // 次幂 BigInteger max(BigInteger val); // 返回两个整数中大的一个 int intValue(BigInteger val);
-
5.7 时间#
一些基本知识:
- 时区,以本初子午线为基准线(时间为00:00:00),划分的24个时区,中国在东八区(08:00:00),比基准线快8个小时
- 1秒钟的判定,用铯原子的跳动方式来计时
传统日期类有:Date(表示日期和时间,可以精确到毫秒)、Calendar(表示日历),但线程不安全
java之后使用新日期时间类:
-
新日期时间
-
时间戳
-
格式化
5.7.1 日期时间#
LocalDate:年月日
LocalTime:时分秒
LocalDateTime:年月日 时分秒
方法:
// now
LocalDate date = LocalDate.now();
LocalTime time = LocalTime.now();
LocalDateTime dateTime = LocalDateTime.now();
// of
LocalDate date = LocalDate.of(2026, 2, 3);
LocalTime time = LocalTime.of(14, 30, 0);
LocalDateTime dateTime = LocalDateTime.of(2026, 2, 3, 14, 30);
// 加减日期、 时间
// 比较前后
// 获取具体的字段
javaZoneId:指定时区(字符串)
ZonedDateTime:带时区的时间
ZoneId zone = ZoneId.of("Asia/Shanghai");
ZonedDateTime zdt = ZonedDateTime.now(zone);java5.7.2 时间戳#
Instant:时间线上的一个瞬时点,精确到纳秒
// now
Instant now = Instant.now();
long millis = now.toEpochMilli();javaDuration:时分秒 时间间隔
Period:年月日 日期间隔
// duration
Duration d = Duration.between(start, end);
long seconds = d.getSeconds();
// period
Period p = Period.between(startDate, endDate);
int days = p.getDays();java5.7.3 格式化#
DateTimeFormatter:格式化类
DateTimeFormatter formatter =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");java格式化时间到字符串
String str = LocalDateTime.now().format(formatter);java格式化字符串到时间
LocalDateTime t = LocalDateTime.parse(
"2026-02-03 14:30:00",
formatter
);java5.8 Arrays#
String toString([] arr); // 数组拼接为字符串
int binarySearch([] arr, elem); // 数组中找elem
int[] copyOf([] src, destLen); // 拷贝数组
int[] copyOfRange([] src, srcStart, srcEnd);
void fill([] arr, elem); // 填充
void sort([] arr);
void sort([] arr, 排序规则);java5.9 Collection#
通用方法:
int size();
boolean isEmpty();
boolean contains(Object o);
boolean add(E e);
boolean remove(Object o);
Object[] toArray();
void clear();
Iterator<E> iterator(); // 获取迭代器java三种遍历方式:
-
迭代器
javaIterator<> it = collection.iterator(); while (it.hasNext()) { it.next(); } -
增强for
javafor (type it : collection) { } -
forEach
javacollection.forEach(new Consumer<>() { @Override public void accept(type s) { // } }); // lambda collection.forEach((type s) -> { // })
5.9.1 ArrayList#
用法类似c++中的vector
boolean add(E e); // push_back(e);
E get(int index); // val = v[index];
E set(int index, E element); // v[index] = val;java5.9.2 LinkedList#
链表结构,api稍有不同
void addFirst(E e);
void addLast(E e);
E getFirst();
E getLast();
E removeFirst();
E removeLast();java5.9.3 HashSet#
套用Collection api
自定义类要重写hashCode方法,否则默认使用地址值
5.9.4 TreeSet#
套用Collection api
默认升序
自定义类需要实现Comparable<E>接口:
class A implements Comparable<A> {
@Override
public int compareTo(A o) {
// return 0, -1, 1
// 0: 相等
// -1: this 比 o 小
// 1: this 比 o 大
// 返回-1, 顺序是this, o
// 返回1, 顺序是o, this
}
}java或者指定TreeSet的排序规则(Comparator<E>):
TreeSet<A> = new TreeSet<A>(new Comparator<A>() {
@Override
public int compare(A o1, A o2) {
// 0: 相等
// -1: o1 < o2
// 1 : o1 > o2
// return 0, -1, 1
// 返回-1, 顺序是o1, o2
// 返回1, 顺序是o2, o1
}
})java还有一些其他的方法,第一个/最后一个元素,树上二分查找等
5.10 Map#
-
通用方法
javaV put(K key, V val); // 添加键值对 V get(Object key); // 取键对应值 V remove(Object key); // 删除键 void clear(); // 清空 boolean containsKey(Object key); // 是否有键 boolean containsValue(Object val); // 是否有值 boolean isEmpty(); // 空 int size(); // 键个数 -
遍历
-
keySet,返回一个键集合,遍历键,获取值
javaSet<Object> keySet = map.keySet(); for (Object obj : keySet { Object val = map.get(obj); } -
entrySet,返回一个键值对集合,直接遍历键值对
javaSet<Map.Entry<Object, Object>> entrySet = map.entrySet(); for (Map.Entry<Object, Object> entry : entrySet) { entry.getKey(); entry.getValue(); } -
forEach,类似Collection,接收键值两个参数
javamap.forEach((Object key, Object value) -> { // });
-
5.10.1 TreeMap#
按键排序,默认升序
自定义排序:
- Key类实现
Comparable<Key>接口 - 指定TreeMap的排序规则(
Comparator<Key>)
第一个,最后一个键值对,树上二分查找等
5.10.2 HashMap#
依赖hashCode()和equals()保证键唯一
5.11 可变参数#
本质是数组
使用方法:func(Object... args)
- 一个方法只能有一个可变参数
- 可变参数在所有参数最后
5.12 Stream链式编程#
目的是简化Collection和数组的操作
5.12.1 创建流#
// Collection
default Stream<E> stream();
// Arrays
public static<T> Stream<T> stream(T[] array);
// Stream
public static<T> Stream<T> of (T... values);java5.12.2 中间方法#
Stream<T> filter(Predicate<? super T> predicate); // 按条件过滤
Stream<T> limit(long maxSize); // 获取前maxSize个元素
Stream<T> skip(long n); // 跳过前n个元素
Stream<T> distinct(); // 元素去重,需要hashCode和equals
Stream<T> concat(Stream a, Stream b); // 合并a,b流为一个流
Stream<T> map(Function<T, R> mapper); //java示例:
-
filter:
javalist.stream().fiter((Object obj) -> { // 过滤掉返回值为false的 // return true; // return false; }); -
map:
javalist.stream().map((Object obj) -> { // 对object做操作, 返回新的数据类型 // 比如将一个String转为Integer, 之后的流上都是Integer类型的数据 return newObj; });
5.12.3 终结方法#
void forEach(Consumer action); // 遍历
long count(); // 统计数据个数
toArray(); // 转为数组
collect(Collector collector); // 转为集合java-
toArray()
将流中的数据封装到
Object[]中返回 -
toArray(IntFunction<A[]> generator)
返回对应类型的数组
javalist.stream().toArray((int value) -> { return new type[value]; }); -
collect(Collector collector)
-
封装到List中返回
javaList<> list = list.stream().collect(Collectors.toList()); -
封装到Set中
javaSet<> set = list.stream().collect(Collectors.toSet()); -
封装到Map中
javaMap<> map = list.stream().collect(Collectors.toMap( (Object obj) -> { // 返回键, 不能重复 }, (Object obj) -> { // 返回值 } ));
-
六. 异常#
c++异常一坨,java更成体系,没有类比的必要了,东西也不多,直接记住就好了

- 编译时异常,编译阶段给提示,必须先手动处理(见6.1-6.2)
- 运行时异常,运行程序可能出现异常
6.1 捕获#
catch支持多级,支持同时捕获多个异常(用|分割)
e.printStackTrace():打印异常堆栈信息
try {
} catch (Exception e) {
e.printStackTrace();
} finally {
}java6.2 抛出#
throws 异常类1, 异常类2:方法定义后,表示使用方法可能会抛出这些异常throw 异常:方法内,手动抛出异常,停止方法运行并将异常返回给调用者
public void method() throws Exception {
throw Exception();
}java6.3 自定义#
- 编译时异常,继承
Exception - 运行时异常,继承
RuntimeException
public class MyException extends RuntimeException {
// 空参
public MyException() {
}
// 带参
public MyException(String messgae) {
super(message)
}
}java6.4 异常资源释放#
类似c++中的RAII思想,自动释放资源(即使出现异常的情况下)
具体做法是:
// 基本语法
try (ResourceType resource = new ResourceType()) {
// 使用资源的代码
} catch (Exception e) {
// 异常处理
}
// 多个资源
try (ResourceType1 resource1 = new ResourceType1();
ResourceType2 resource2 = new ResourceType2()) {
// 使用资源的代码
} catch (Exception e) {
// 异常处理
}java其中,ResourceType必须实现AutoCloseable接口
七. IO流#
-
按数据单位分:字节流、字符流
-
按流向分:输入流、输出流
-
按角色分类:节点流、包装流
7.1 字节流#
以Stream结尾的各类对象都是字节流:
7.2 字符流#
以Reader和Writer结尾的各类对象都是字符流
7.3 文件#
7.4 序列化#
八. 多线程#
8.1 创建方式:#
-
继承
Thread类,重写run方法 (简单任务) -
实现
Runnable接口,重写run方法 (要执行的方法与线程分离)
javaclass MyTask implements Runnable { @Override public void run() { System.out.println("任务执行中..."); } } // 使用 Thread thread = new Thread(new MyTask()); thread.start(); -
实现
Callable<T>接口,重写call方法 (支持返回值,能抛异常,配合Future使用)
javaclass MyCallable implements Callable<String> { @Override public String call() throws Exception { return "任务执行结果"; } } // 使用 FutureTask<String> task = new FutureTask<>(new MyCallable()); Thread thread = new Thread(task); thread.start(); String result = task.get(); // 获取结果
8.2 锁#
在java里面,任何一个对象都可以被当做mutex来使用,
九. 反射#
加载类,并解剖类中的信息
9.1 获取Class对象#
Class.forName("package.className"):填全类名className.class:做参数传递obj.getClass():能创建对象再考虑使用
9.2 常用方法#
- 获取构造器
Constructor对象,并利用其构造类对象 - 获取成员变量
Field对象,获取/修改类成员变量 - 获取方法
Method对象,执行类方法
获取Constructor,Field,Method类型的对象,大部分签名都很类似,挑重点的记:
-
getName:获取类名 -
getDeclaredConstructors():返回可用的Constructor数组getDeclaredConstructor(xx1.class, xx2.class):返回对应有参的Constructor对象其他,要获取对应类的成员变量和方法,其get方法也类似上面
-
使用
Constructor对象构造原对象
javaClass c1 = ArrayList.class; // 获取无参构造器对象(可能是 private,因此使用 getDeclaredConstructor) Constructor con = c1.getDeclaredConstructor(); // 临时设置访问权限,使得可以访问 private 构造函数 con.setAccessible(true); // 构造对象, 需要强转,因为返回的是 Object ArrayList s1 = (ArrayList) con.newInstance(); -
使用
Field对象设置/获取类成员变量
javaClass c1 = ArrayList.class; // 获取 ArrayList 类中的对应成员变量,例如 "size" Field fieldSize = c1.getDeclaredField("size"); // 临时关闭 Java 权限检查 fieldSize.setAccessible(true); // arr 是一个 ArrayList 的对象 ArrayList arr = new ArrayList(); // 设置字段值为指定内容 fieldSize.set(arr, 10); // 获取字段值 int var = (int) fieldSize.get(arr); System.out.println("size = " + var); -
使用
Method对象调用类方法
javaClass c1 = String.class; // 获取 private 方法,如 String 中的 "indexOfSupplementary" Method method = c1.getDeclaredMethod("indexOfSupplementary", int.class, int.class); // 开放权限 method.setAccessible(true); // 调用方法,需要传入对象和参数 String s = "hello"; int result = (int) method.invoke(s, 0x10400, 0); System.out.println(result);
作用:
-
获取一个类的全部成分然后操作
-
破坏封装性
-
绕过泛型,比如在
ArrayList<Integer>添加"123" -
框架
十. 注解#
10.1 定义#
public @interface MyAnnotation {
public String name1() default "123";
}java本质是一个接口,自定义注解是继承了Annotation接口的接口
-
使用
默认情况下可以在类名,方法名,变量名上直接加
@MyAnnotation使用,这代表接口的一个实现类 -
要指定里面的属性值就用
@MyAnnotation(name1="", xxx),如果注解里面非默认属性只有一个value,可以直接把value=省略
元注解
注解在注解上的注解
形式:
@Rentention
@Target
public @interface MyAnnotation {
public String name1() default "123";
}javaRentention:声明注解的保留周期@Rentention(RententionPolicy.RUNTIME):一直保留,常用RententionPolicy.SOURCE:源码保留RententionPolicy.CLASS:字节码保留
Target:限制注解的范围@Target(ElementType.Type):该注解可以在类和接口上使用ElementType.FIELD:成员变量ElementType.METHOD:成员方法ElementType.PARAMETER:方法参数
10.2 解析#
利用反射
-
获取使用注解的类对象
Class c1 = Demo.class -
判断对应注解是否存在
c1.isAnnotationPresent(MyAnnotation.class) -
获取注解对象并获取其属性值
MyAnnotation myan = (MyAnnotation) getDeclaredAnnotation(MyAnnotation.class)
Class,Method,Field,Constructor等都实现了AnnotatedElement接口:
public Annotation[] getDeclaredAnnotations(); // 获取当前对象上的注解
public T getDeclaredAnnotation(Class<T> annotationClass); // 获取指定的注解对象
public boolean isAnnotationPresent(Class<Annotation> annotationClass); // 判断当前对象是否存在某个注解java使用场景:
比如JUnit框架定义的@Test,加上就测试,本质就是底层利用反射,看Method上是否有@Test注解,决定是否invoke
十一. 动态代理#
像是一个包装类,使用非侵入式的形式来对被代理的类增添功能代码(底层使用反射)
核心是Proxy.newPorxyInstance,简易代码如下,invoke中的proxy指的是T proxy:
class ProxyUtil {
public static <T> T createProxy(T obj) {
T proxy = (T) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),
obj.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// do something before method...
Object result = method.invoke(obj, args);
// do something after method...
return result; // return method result
}
});
return proxy;
}
}java关于Proxy.newPorxyInstance:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)javaClassLoader loader:指定加载代理类的类加载器Class<?>[] interfaces:指定代理要实现的接口,一般被代理的类实现哪些,就填哪些InvocationHandler h:匿名内部类,在内部指定生成的代理类对象要增加的功能