目录
类型信息
- 可以通过反射获取类运行时的类型信息 RTTI Class类
- 类字面常量 Test.class 加载 链接(在链接阶段将验证类中的字节码,为 static 字段分配存储空间,并且如果需要的话,将解析这个类创建的对其他类的所有引用) 初始化
.class 语法来获得对类对象的引用不会引发初始化。但与此相反,使用 Class.forName() 来产生 Class 引用会立即就进行初始化(调用static,但不会调用构造函数)。如果一个 static final 值是“编译期常量”(如 Initable.staticFinal),那么这个值不需要对 Initable 类进行初始化就可以被读取,对 Initable.staticFinal2 的访问将强制进行类的初始化(调用static,但不会调用构造函数),因为它不是一个编译期常量 - 泛型
// Integer 继承自 Number。但事实却是不行,因为 Integer 的 Class 对象并不是 Number的 Class 对象的子类
Class<Number> geenericNumberClass = int.class;
// work 使用 Class<?> 的好处是它表示你并非是碰巧或者由于疏忽才使用了一个非具体的类引用,而是特意为之
Class<?> intClass = int.class;
intClass = double.class;
// 限制了
Class<? extends Number> bounded = int.class;
Class.isInstance() 方法提供了一种动态测试对象类型的方法。因此,所有这些繁琐的 instanceof
- RTTI 。RTTI 的含义所在:在运行时,识别一个对象的类型,接下来就是多态机制的事了,Shape 对象实际执行什么样的代码,是由引用所指向的具体对象(Circle、Square 或者 Triangle)决定的。使用 RTTI,我们可以查询某个 Shape 引用所指向对象的确切类型,然后选择或者剔除特例
- Java 是如何在运行时识别对象和类信息的。主要有两种方式:
- “传统的” RTTI:假定我们在编译时已经知道了所有的类型;
- “反射”机制:允许我们在运行时发现和使用类的信息。
- 如果基类来自别人的库,这时 RTTI 便是一种解决之道:可继承一个新类,然后添加你需要的方法。在代码的其它地方,可以检查你自己特定的类型,并调用你自己的方法
泛型
- ArrayList<String> 和 ArrayList<Integer> 在运行时实际上是相同的类型,它们都被擦除成原生类型 List。(擦除的核心动机是你可以在泛化的客户端上使用非泛型的类库)但是 String不能添加Integer,编译器就会报错。以下这个不会报错
GenericBase<T>
class Derived2 extends GenericBase {} // No warning
- 泛型的所有动作都发生在边界处——对入参的编译器检查和对返回值的转型。这有助于澄清对擦除的困惑,记住:“边界就是动作发生的地方”
- T[] array = new T[SIZE] 会报错(部分原因是由于擦除,部分原因是编译器无法验证 T 是否具有默认(无参)构造函数)
- 协变, 逆变
// 这里更多的是读取
Class<? extends Number> bounded = int.class;
// 如果不知道 List 中持有的对象是什么类型,你怎能保证安全地向其中添加对象呢, 只能加null
// 如果是T泛型就OK,因为知道具体类型 使用通配符可以将ArrayList<Apple>向上转型了,也就实现了协变
//List<? extends Number> x3 = new ArrayList<>();
// 超类通配符可以写入 这是逆变 这样编译器就知道向其中添加Apple或Apple的子类型(例如Jonathan)是安全的了
List<? super Number> x = new ArrayList<>();
Integer x1 = 3;
x.add(x1);
- class SelfBounded<T extends SelfBounded<T>>
自限定所做的,就是要求在继承关系中,像下面这样使用这个类:
class A extends SelfBounded<A>{}
它可以保证类型参数必须与正在被定义的类相同
- 自限定类型的价值在于它们可以产生协变参数类型——方法参数类型会随子类而变化
class Base {}
class Derived extends Base {}
interface OrdinaryGetter {
Base get();
}
interface DerivedGetter extends OrdinaryGetter {
// Overridden method return type can vary: 协变
@Override
Derived get();
}
public class CovariantReturnTypes {
void test(DerivedGetter d) {
Derived d2 = d.get();
}
}
- 泛型异常
interface Processor<T, E extends Exception> {
void process(List<T> resultCollector) throws E;
}
String
- string+ 会被编译器优化成StringBuild append方法
- 不可变性体现在每次都返回一个新对象
- StringTokenizer可做分词用,有了正则和Scanner之后这个类基本不用
数组
- Arrays.copyOf原生数组和对象数组都可以被复制。但是,如果复制对象的数组,那么只复制引用—不复制对象本身。这称为浅拷贝。只复制了对象的引用(内存地址),并没有为每个元素新创建对象,虽然是个新数组。底层System.arraycopy()。
- System.arraycopy() ,它将一个数组复制到另一个已经分配的数组中。这将不会执行自动装箱或自动卸载—两个数组必须是完全相同的类型。System.arraycopy 对于数组是深拷贝,对于数组内的对象是浅拷贝。因为操作的传入参数是数组,那么回归本意,效果是深复制。
- Arrays.parallelSort(la) 并发排序
- 一旦数组被排序,您就可以通过使用 Arrays.binarySearch()
- Arrays parallelPrefix()。这类似于 Stream 类中的 reduce() 方法:它对前一个元素和当前元素执行一个操作,并将结果放入当前元素位置
枚举:
- 不能继承自一个 enum
- enum 都继承自 Java.lang.Enum 类。由于 Java 不支持多重继承,所以你的 enum 不能再继承其他类
- import static enums.AlarmPoints.*;可以简化操作
- EnumSet(Enum的set集合) 的基础是 long,一个 long 值有 64 位,而一个 enum 实例只需一位 bit 表示其是否存在。 也就是说,在不超过一个 long 的表达能力的情况下,你的 EnumSet 可以应用于最多不超过 64 个元素的 enum, EnumSet 可以应用于多过 64 个元素的 enum,所以我猜测,Enum 会在必要的时候增加一个 long
// public enum AlarmPoints
EnumSet<AlarmPoints> points = EnumSet.noneOf(AlarmPoints.class); // Empty
points.add(BATHROOM);
EnumSet是高性能的基于位运算
- EnumMap 是一种特殊的 Map,它要求其中的键(key)必须来自一个 enum,EnumMap速度很快
- 多路分发就是指在调用a.plus(b),a和b都不知道确切类型,也能让他们正常交互
java实现多路分发
注解:
- @FunctionalInterface:Java 8 中加入用于表示类型声明为函数式接口
- 注解元信息
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {}
@Target 定义你的注解可以应用在哪里(例如是方法还是字段)
@Retention 定义了注解在哪里可用,在源代码中(SOURCE),class文件(CLASS)中或者是在运行时(RUNTIME)
@Documented 将此注解保存在 Javadoc 中
@Repeatable 允许一个注解可以被使用一次或者多次(Java 8)
- 反射处理注解
for(Method m : cl.getDeclaredMethods()) {
UseCase uc = m.getAnnotation(UseCase.class);
- aop加注解的形势
@Around("@annotation(****.common.annotation.PreventContinueClick)")
- 写注解时加default
String description() default ""
- 不能使用 extends 关键字来继承 @interfaces
- @Retention(RetentionPolicy.SOURCE)
当你创建用于 javac 注解处理器时,你不能使用 Java 的反射特性,因为你处理的是源代码,而并非是编译后的 class 文件 - Javac 是一种编译器,能将一种语言规范转化成另外一种语言规范
- Javadoc用于描述类或者方法的作用
参考文章
- java编程思想 第五版
- java实现多路分发