# String

# String.format

format组成及含义--传送门 (opens new window)

// 左对齐,10位宽
String.format("%-10s",foo.getGoodsName());

# 集合

# List

List是非常常用的数据类型,是有序的Collection,常用的实现类分别是ArrayList、Vector和LinkedList。

# ArrayList

基于数组实现,增删慢,查询快,线程不安全

ArrayList的缺点是对元素必须连续存储,当需要在ArrayList的 中间位置插入或者删除元素时,需要将待插入或者删除的节点后的所 有元素进行移动,其修改代价较高,因此,ArrayList不适合随机插入和删除的操作,更适合随机查找和遍历的操作。

ArrayList不需要在定义时指定数组的长度,在数组长度不能满足 存储要求时,ArrayList会创建一个新的更大的数组并将数组中已有的数据复制到新的数组中。

# Vector

基于数组实现,增删慢,查询快,线程安全

为保证多线程环境下数据的一致性,需要频繁地对Vector实例进行加锁和释放锁操作,因此,Vector的读写效率在整体上比ArrayList低。

# LinkedList

基于双向链表实现,增删快,查询慢,线程不安 全

LinkedList采用双向链表结构存储元素,在对LinkedList进行插 入和删除操作时,只需在对应的节点上插入或删除元素,并将上一个 节点元素的下一个节点的指针指向该节点即可,数据改动较小,因此 随机插入和删除效率很高。但在对LinkedList进行随机访问时,需要 从链表头部一直遍历到该节点为止,因此随机访问速度很慢。

。除此之 外,LinkedList还提供了在List接口中未定义的方法,用于操作链表 头部和尾部的元素,因此有时可以被当作堆栈、队列或双向队列使 用。

# Queue

Queue是队列结构。

LinkedList还提供了在List接口中未定义的方法,用于操作链表 头部和尾部的元素,因此有时可以被当作堆栈、队列或双向队列使 用。

# Set

Set核心是独一无二的性质,适用于存储无序且值不相等的元素。 对象的相等性在本质上是对象的HashCode值相同,Java依据对象的内 存地址计算出对象的HashCode值。

# HashSet

基于HashMap实现,k为HashSet的元素,v统一为new Object();

# TreeSet

基于TreeMap实现,k为HashSet的元素,v统一为new Object();

# LinkedHashSet

基于LinkedHashMap实现,k为HashSet的元素,v统一为new Object();

# Map

List、Queue、Set都是基于索引寻找元素,Map则基于别名寻找元素。

# HashMap

数组+链表存储数据,线程不安全

HashMap基于键的HashCode值唯一标识一条数据,因此可以快速地更新和查询数据,但其每次遍历的顺序无法保证相同。HashMap的key和value允许为null。

HashMap是非线程安全的,即在同一时刻有多个线程同时写HashMap时将可能导致数据的不一致。如果需要满足线程安全的条件, 则可以用Collections的synchronizedMap方法使HashMap具有线程安全的能力,或者使用ConcurrentHashMap。

# HashTable

HashTable是遗留类,很多映射的常用功能都与HashMap类似,不 同的是它继承自Dictionary类,并且是线程安全的,同一时刻只有一个线程能写HashTable,并发性不如ConcurrentHashMap。

较之于Vector与List,之于HashTable于Map。

# TreeMap

TreeMap基于二叉树数据结构存储数据,同时实现了SortedMap接 口以保障元素的顺序存取,默认按键值的升序排序,也可以自定义排序比较器。

TreeMap常用于实现排序的映射列表。在使用TreeMap时其key必须 实 现 Comparable 接 口 或 采 用 自 定 义 的 比 较 器 , 否 则 会 抛 出java.lang.ClassCastException异常。

# LinkedHashMap

LinkedHashMap为HashMap的子类,其内部使用链表保存元素的插 入顺序,在通过Iterator遍历LinkedHashMap时,会按照元素的插入顺序访问元素。

public void forEach(BiConsumer<? super K, ? super V> action) {
    if (action == null)
        throw new NullPointerException();
    int mc = modCount;
    for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after)
        action.accept(e.key, e.value);
    if (modCount != mc)
        throw new ConcurrentModificationException();
}

# 异常

异常就是方法的非正常退出。

异常指在方法不能按照正常方式完成时,可以通过抛出异常的方 式退出该方法,在异常中封装了方法执行过程中的错误信息及原因, 调用方在获取该异常后可根据业务的情况选择处理该异常或者继续抛出该异常。

在方法在执行过程中出现异常时,Java异常处理机制会将代码的 执行权交给异常处理器,异常处理器根据在系统中定义的异常处理规则执行不同的异常处理逻辑(抛出异常或捕捉并处理异常)。

CheckedException(受检异常)只是必须被开发人员处理的异常,要么try catch 捕获,要么在方法签名处声明,进行抛出。

# 反射

反射机制指在程序运行过程中,动态 获取类和对象的信息,进行对象创建、属性修改、方法调用的机制。

# 动态语言

动态语言指程序在运行时可以改变其结构的语言,比如新的属性 或方法的添加、删除等结构上的变化。JavaScript、Ruby、Python等 都属于动态语言;C、C++不属于动态语言。从反射的角度来说,Java属于半动态语言。

# 运行时类型

Java中的对象有两种类型:编译时类型和运行时类型。

编译时类 型指在声明对象时所采用的类型,运行时类型指为对象赋值时所采用的类型。

在如下代码中,person对象的编译时类型为Person,运行时类型为Student,因此无法在编译时获取在Student类中定义的方法:

Person person = new Student();

因此,程序在编译期间无法预知该对象和类的真实信息,只能通 过运行时信息来发现该对象和类的真实信息,而其真实信息(对象的 属性和方法)通常通过反射机制来获取,这便是Java语言中反射机制的核心功能。

# 反射API

◎ Class类:用于获取类的属性、方法等信息。

◎ Field类:表示类的成员变量,用于获取和设置类中的属性值。

◎ Method类:表示类的方法,用于获取方法的描述信息或者执行某个方法。

◎ Constructor类:表示类的构造方法。

# 获取Class对象

(1)调用某个对象的getClass方法以获取该类对应的Class对象:

Person p = new Person();
Class cls = p.getClass();

(2)调用某个类的class属性以获取该类对应的Class对象:

Class cls = Person.class;

(3)调用Class类中的forName静态方法以获取该类对应的Class对象,这是最安全、性能也最好的方法:

Class cls = Class.forName("fullClassPath");

# 创建对象

利用反射机制创建对象有两种方式。

◎ 使用Class对象的newInstance方法创建该Class对象对应类的实例,这种方法要求该Class对象对应的类有默认的空构造器。

◎ 先 使 用 Class 对 象 获 取 指 定 的 Constructor 对 象 , 再 调 用Constructor对象的newInstance方法创建Class对象对应类的实例,通过这种方法可以选定构造方法创建实例。

# 动态代理

Proxy

InvocationHandler


public class MyInvocationHandler implements InvocationHandler{
    // 目标对象
    private Object target;
    
    public MyInvocationHandler(Object target){
        this.target = target;
    }
    
    @Override
    public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
        // 前置增强
        ...
        Object result = method.invoke(target,args);
        // 后置增强
        ...
        return result;
    }
}

To create a proxy for some interface Foo:
        
InvocationHandler handler = new MyInvocationHandler(...);
Class<?> proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), Foo.class);
Foo f = (Foo) proxyClass.getConstructor(InvocationHandler.class).
               newInstance(handler);
   
or more simply:
        
Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),new Class<?>[] { Foo.class },handler);

# 注解

注解是Java提供的为类中元素添加附加信息的机制。

程序可以通过反射获取 指定程序中元素的注解对象,然后通过该注解对象获取注解中的元数据信息。

# 元注解

元注解(Meta-Annotation)负责注解其他注解。在Java中定义了 4 个 标 准 的 元 注 解 类 型 @Target 、 @Retention 、 @Documented 、@Inherited,用于定义不同类型的注解。

(1)@Target:@Target说明了注解所修饰的对象范围。注解可被 用于packages、types(类、接口、枚举、注解类型)、类型成员(方 法、构造方法、成员变量、枚举值)、方法参数和本地变量

(2)@Retention:@Retention定义了该注解被保留的级别,即被描述的注解在什么级别有效,有以下3种类型。

◎ SOURCE:在源文件中有效,即在源文件中被保留。

◎ CLASS:在Class文件中有效,即在Class文件中被保留。

◎ RUNTIME:在运行时有效,即在运行时被保留。

(3)@Documented:@Documented表明这个注解应该被javadoc工具记录,因此可以被javadoc类的工具文档化。

(4)@Inherited:@Inherited是一个标记注解,表明某个被标注 的类型是被继承的。如果有一个使用了@Inherited修饰的Annotation被用于一个Class,则这个注解将被用于该Class的子类。

# 注解处理器

# 内部类

定义在类内部的类被称为内部类。内部类根据不同的定义方式,可分为静态内部类、成员内部类、局部内部类和匿名内部类这4种。

# 静态内部类

定义在类内部的静态类被称为静态内部类。静态内部类可以访问 外部类的静态变量和方法;在静态内部类中可以定义静态变量、方 法、构造函数等;静态内部类通过“外部类.静态内部类”的方式来调用

# 成员内部类

定义在类内部的非静态类叫作成员内部类,成员内部类不能定义 静态方法和变量(final修饰的除外),因为成员内部类是非静态的, 而在Java的非静态代码块中不能定义静态方法和变量。

# 局部内部类

定义在方法中的类叫作局部内部类。当一个类只需要在某个方法 中使用某个特定的类时,可以通过局部类来优雅地实现

# 匿名内部类

匿名内部类指通过继承一个父类或者实现一个接口的方式直接定 义并使用的类。匿名内部类没有class关键字,这是因为匿名内部类直接使用new生成一个对象的引用。

btn.setOnClickListener(new OnClickListener(){
    //...
});

# 泛型

参数化类型。

便于定义通用的类、接口、方法。

# 泛型检查

在不使用泛型的情况下,我们可以通过引用Object类型来实现参 数的任意化,因为在Java中Object类是所有类的父类,但在具体使用 时需要进行强制类型转换。

强制类型转换要求开发者必须明确知道实 际参数的引用类型,不然可能引起前置类型转换错误,在编译期无法 识别这种错误,只能在运行期检测这种错误(即只有在程序运行出错 时才能发现该错误)。而使用泛型的好处是在编译期就能够检查类型 是否安全,同时所有强制性类型转换都是自动和隐式进行的,提高了代码的安全性和重用性。

# 泛型边界

1.对泛型上限的限定:

<? extends T>Java中使用通配符“?”和“extends”关键字指定泛型的上 限,具体用法为<? extends T>,它表示该通配符所代表的类型是T类的子类或者接口T的子接口。

2.对泛型下限的限定:

<? super T>Java中使用通配符“?”和“super”关键字指定泛型的下限, 具体用法为<? super T>,它表示该通配符所代表的类型是T类型的父类或者父接口。

# 泛型擦除

在编码阶段采用泛型时加上的类型参数,会被编译器在编译时去 掉,这个过程就被称为类型擦除。因此,泛型主要用于编译阶段。在 编译后生成的Java字节代码文件中不包含泛型中的类型信息。例如, 编码时定义的List<Integer>List<String>在经过编译后统一为List。JVM所读取的只是List,由泛型附加的类型信息对JVM来说是不可见的。

Java类型的擦除过程为:首先,查找用来替换类型参数的具体类 (该具体类一般为Object),如果指定了类型参数的上界,则以该上 界作为替换时的具体类;然后,把代码中的类型参数都替换为具体的类。

擦除动机: 客户端程序引用了类库A,此时类库A是未泛型的。 如果类库A泛型改造了,客户端程序就会报错。 类库A的泛型被擦除了,则客户端不会报错。

# 序列化

序列化是为了传输共享、持久化对象的状态。

Java对象在JVM运行时被创建、更新和销毁,当JVM退出时,对象 也会随之销毁,即这些对象的生命周期不会比JVM的生命周期更长。

但 在现实应用中,我们常常需要将对象及其状态在多个应用之间传递、 共享,或者将对象及其状态持久化,在其他地方重新读取被保存的对象及其状态继续进行处理。这就需要通过将Java对象序列化来实现。

# 序列化API

Java序列化API为处理对象序列化提供了一个标准机制,具体的Java系列化需要注意以下事项。

◎ 类要实现序列化功能,只需实现java.io.Serializable接口即可。

◎ 序列化和反序列化必须保持序列化的ID 一致,一般使用private static final long serialVersionUID定义序列化ID。

◎ 序列化并不保存静态变量。

◎ 在需要序列化父类变量时,父类也需要实现Serializable接口。

◎ 使用transient关键字可以阻止该变量被序列化,在被反序列 化后,transient变量的值被设为对应类型的初始值,例如,int类型变量的值是 0,对象类型变量的值是null。

# 反序列化

在Java生态中有很多优秀的序列化框架,比如arvo、protobuf、thrift、fastjson。我们也可以基于JDK原生的ObjectOutputStream和ObjectInputStream类实现对象进行序列化及反序列化

Last Updated: 6 days ago