Start with very simple idea
<1> equals与==的区别
1. ==是一个运算符 equals是Object类的方法区别
2. 比较时的区别用于
基本类型的变量比较时: ==用于比较值是否相等,equals不能直接用于基本数据类型的比较,需要转换为其对应的包装类型。
用于引用类型的比较时: ==和equals都是比较栈内存中的地址是否相等 。相等为true 否则为false。但是通常会重写equals方法去实现对象内容的比较。
<2> Object.equals()
The default implementation of the equals() method in the Object class compares the references of the two objects to see if they are the same object in memory.
<3> hashCode
hashCode() 有什么用 与 a.equals(b) 有什么关系?简介: hashCode() 方法是相应对象整型的 hash 值。它常用于基于 hash 的集合类,如 Hashtable、HashMap、LinkedHashMap等等。它与 equals() 方法关系特别紧密。 根据 Java 规范,两个使用 equals() 方法来判断相等的对象,必须具有相同的 hash code。
除了传统的手动写hashCode, 还可以用Objects.hash(Object...)
<4> Comparator 与 Comparable 有什么不同
Comparable 接口用于定义对象的自然顺序,而 comparator 通常用于定义用户定制的顺序。
Comparable 总是只有一个,但是可以有多个 comparator 来定义对象的顺序
<5> Object有哪些公用方法
clone equals hashcode wait notify notifyall finalize toString getClass 除了clone和finalize其他均为公共方法。11个方法,wait被重载了两次
<6> 关于finally
finally不管有没有异常都要处理当try和catch中有return时,finally仍然会执行,finally比return先执行不管有木有异常抛出, finally在return返回前执行
finally是在return后面的表达式运算后执行的(此时并没有返回运算后的值,而是先把要返回的值保存起来,管finally中的代码怎么样,返回的值都不会改变,仍然是之前保存的值),
所以函数返回值是在finally执行前确定的注意: finally中最好不要包含return,否则程序会提前退出,返回值不是try或catch中保存的返回值finally不执行的几种情况: 程序提前终止如调用了System.exit, 病毒,断电
<7> 序列化
声明为static和transient类型的数据不能被序列化, 反序列化需要一个无参构造函数
<8> Java 中,Serializable 与 Externalizable 的区别
Serializable 接口是一个序列化 Java 类的接口,以便于它们可以在网络上传输或者可以将它们的状态保存在磁盘上,是 JVM 内嵌的默认序列化方式,成本高、脆弱而且不安全。Externalizable 允许你控制整个序列化过程,指定特定的二进制格式,增加安全机制。
<9> Java 中的拷贝
浅拷贝
拷贝对象和原始对象的引用类型引用同一个对象
深拷贝
拷贝对象和原始对象的引用类型引用不同对象
clone() 的替代方案使用 clone() 方法来拷贝一个对象即复杂又有风险,它会抛出异常,并且还需要类型转换。
Effective Java 书上讲到,最好不要去使用 clone(),可以使用拷贝构造函数或者拷贝工厂来拷贝一个对象
<10> Java 中的static
1. 静态变量
静态变量: 又称为类变量,也就是说这个变量属于类的,类所有的实例都共享静态变量,可以直接通过类名来访问它;静态变量在内存中只存在一份。实例变量: 每创建一个实例就会产生一个实例变量,它与该实例同生共死
2. 静态方法 静态方法在类加载的时候就存在了,它不依赖于任何实例。所以静态方法必须有实现,也就是说它不能是抽象方法(abstract)。
3. 静态语句块 静态语句块在类初始化时运行一次。
存在继承的情况下,初始化顺序为:
父类(静态变量、静态语句块)
子类(静态变量、静态语句块)
父类(实例变量、普通语句块)
父类(构造函数)
子类(实例变量、普通语句块)
子类(构造函数)
<11> Java 中 i = i + j vs i += j 的可能区别
A compound assignment expression of the form E1 op= E2 is equivalent to E1 = (T)((E1) op (E2)), where T is the type of E1, except that E1 is evaluated only once
例如:
byte i = 127
byte j = 127
则 i = i + j 会报错, 而 i += j 不会
i += j 得到 i = -2
又例如:
As always with these questions, the JLS holds the answer. In this case §15.26.2 Compound Assignment Operators. An extract:
A compound assignment expression of the form
E1 op= E2
is equivalent toE1 = (T)((E1) op (E2))
, whereT
is the type ofE1
, except thatE1
is evaluated only once.
An example cited from §15.26.2
[...] the following code is correct:
short x = 3; x += 4.6;
and results in x having the value 7 because it is equivalent to:
short x = 3; x = (short)(x + 4.6);
<12> 泛型方法
泛型方法
泛型方法,是在调用方法的时候指明泛型的具体类型。
重点看下泛型的方法 说明一下,定义泛型方法时,必须在返回值前边加一个,来声明这是一个泛型方法,持有一个泛型T,然后才可以用泛型T作为方法的返回值
为什么要使用泛型方法呢?
因为泛型类要在实例化的时候就指明类型,如果想换一种类型,不得不重新new一次,可能不够灵活;而泛型方法可以在调用的时候指明类型,更加灵活
<13> abstract class versus interface
Abstract Class
An abstract class is a class that is declared abstract — it may or may not include abstract methods.
Abstract classes cannot be instantiated, but they can be subclassed.
An abstract class may have static fields and static methods.
When an abstract class is subclassed, the subclass usually provides implementations for all of the abstract methods in its parent class.
However, if it does not, then the subclass must also be declared abstract.
An abstract method is a method that is declared without an implementation (without braces and followed by a semicolon), like this:
abstract void sum(int a, int b);
Consider using abstract classes if any of these statements apply to your situation:
-
You want to share code among several closely related classes.
-
You expect that classes that extend your abstract class have many common methods or fields or require access modifiers other than public (such as protected and private).
-
You want to declare non-static or non-final fields. This enables you to define methods that can access and modify the state of the object to which they belong.
Interface
An interface is just the declaration of methods of an object; it’s not the implementation.
In an interface, we define what kind of operation an object can perform.
These operations are defined by the classes that implement the interface.
Interfaces form a contract between the class and the outside world, and this contract is enforced at build time by the compiler.
interface Vehical {
// declaration
void changeGear(int newValue);
void speedUp(int increment);
void applyBrakes(int decrement);
}
class Car implements Vehical {
int speed = 0;
int gear = 1;
// implementation
void changeGear(int newValue) {
gear = newValue;
}
void speedUp(int increment) {
speed = speed + increment;
}
void applyBrakes(int decrement) {
speed = speed - decrement;
}
void printStates() {
System.out.println(" speed:" + speed + " gear:" + gear);
}
}
Consider using interfaces if any of these statements apply to your situation:
-
You expect that unrelated classes would implement your interface. For example, the interfaces Comparable and Cloneable are implemented by many unrelated classes.
-
You want to specify the behavior of a particular data type, but not concerned about who implements its behavior.
-
You want to take advantage of multiple inheritances.
<14> 无限制通配符
无限制通配符 extends 关键字声明了类型的上界,表示参数化的类型可能是所指定的类型,或者是此类型的子类 super 关键字声明了类型的下界,表示参数化的类型可能是指定的类型,或者是此类型的父类 // 使用原则《Effictive Java》 // 为了获得最大限度的灵活性,要在表示 生产者或者消费者 的输入参数上使用通配符,使用的规则就是:生产者有上限、消费者有下限 1. 如果参数化类型表示一个 T 的生产者,使用 < ? extends T>; 2. 如果它表示一个 T 的消费者,就使用 < ? super T>; 3. 如果既是生产又是消费,那使用通配符就没什么意义了,因为你需要的是精确的参数类型。 再看一个实际例子 /* 上述代码中的类型参数 E 的范围是>, 我们可以分步查看: 要进行比较,所以 E 需要是可比较的类,因此需要 extends Comparable<…>(注意这里不要和继承的 extends 搞混了,不一样) Comparable< ? super E> 要对 E 进行比较,即 E 的消费者,所以需要用 super 而参数 List< ? extends E> 表示要操作的数据是 E 的子类的列表,指定上限,这样容器才够大 */ private > E max(List e1) { if (e1 == null){ return null; } Iterator iterator = e1.iterator(); E result = iterator.next(); while(iterator.hasNext()) { E next = iterator.next(); if (next.compareTo(result) > 0) { result = next; } } return result; } //多个限制 // //使用&符号 public static String discount(T data) { return data.compareTo(1) > 0 ? "Discounted" : "Not Discounted"; } ### **<15> 泛型的类型擦除原则是:** * 根据类型参数的上下界推断并替换所有的类型参数为原生态类型: * 如果类型参数是无限制通配符或没有上下界限定则替换为Object, * 如果存在上下界限定则根据子类替换原则取类型参数的最左边限定类型(即父类) 如何证明类型的擦除呢 <1> 原始类型相等 list1.getClass() == list2.getClass(); //true <2> 通过反射添加其它类型元素 list.getClass().getMethod("add", Object.class).invoke(list, "asd"); 在程序中定义了一个ArrayList泛型类型实例化为Integer对象,如果直接调用add()方法,那么只能存储整数数据,不过当我们利用反射调用add()方法的时候,却可以存储字符串,这说明了Integer泛型实例在编译之后被擦除掉了,只保留了原始类型 ### **<16> 如何理解基本类型不能作为泛型类型** 比如,我们没有ArrayList,只有ArrayList, 为何? 因为当类型擦除后,ArrayList的原始类型变为Object,但是Object类型不能存储int值,只能引用Integer的值。 另外需要注意,我们能够使用list.add(1)是因为Java基础类型的自动装箱拆箱操作。 ### **<17> 如何理解泛型类型不能实例化** 不能实例化泛型类型, 这本质上是由于类型擦除决定的: 我们可以看到如下代码会在编译器中报错: T test = new T(); // ERROR 因为在 Java 编译期没法确定泛型参数化类型,也就找不到对应的类字节码文件,所以自然就不行了, 此外由于T 被擦除为 Object,如果可以 new T() 则就变成了 new Object(),失去了本意。 如果我们确实需要实例化一个泛型,应该如何做呢?可以通过反射实现: static T newTclass (Class < T > clazz) throws InstantiationException, IllegalAccessException { T obj = clazz.newInstance(); return obj; } ### **<18> 泛型数组:如何正确的初始化泛型数组实例** 因为在 Java 中是不能创建一个确切的泛型类型的数组的,除非是采用通配符的方式且要做显式类型转换才可以 public class ArrayWithTypeToken { private T[] array; public ArrayWithTypeToken(Class type, int size) throws Exception { this.array = (T[])Array.newInstance(type, size); } public void put(int index, T item) { array[index] = item; } public T get(int index) { return array[index]; } public T[] create() { return array; } } 使用方法: ArrayWithTypeToken arrayWithTypeToken = new ArrayWithTypeToken<>(Integer.class, 10); Integer[] array = arrayWithTypeToken.create(); ### **<19> 如何理解泛型类中的静态方法和静态变量** 泛型类中的静态方法和静态变量不可以使用泛型类所声明的泛型类型参数 public class Test2 { public static T one; //编译错误 public static T show(T one){ //编译错误 return null; } } 但是要注意区分下面的一种情况: public class Test2 { public static T show(T one){ //这是正确的 return null; } } 因为这是一个泛型方法,在泛型方法中使用的T是自己在方法中定义的 T,而不是泛型类中的T ### **<20> 如何理解异常中使用泛型** 不能抛出也不能捕获泛型类的对象。事实上,泛型类扩展Throwable都不合法。例如:下面的定义将不会通过编译: public class Problem extends Exception { } 为什么不能扩展Throwable,因为异常都是在运行时捕获和抛出的,而在编译的时候,泛型信息全都会被擦除掉 不能再catch子句中使用泛型变量 public static void doWork(Class t){ try { } catch(T e) { //编译错误 } catch(IndexOutOfBounds e) { } } 但是在异常声明中可以使用类型变量。下面方法是合法的 public static void doWork(T t) throws T { try{ ... } catch(Throwable realCause) { t.initCause(realCause); throw t; } } ### **<21> 关于泛型数组** //泛型数组 /* List[] list11 = new ArrayList[10]; //编译错误,非法创建 List[] list12 = new ArrayList[10]; //编译错误,需要强转类型 List<String>[] list13 = (List<String>[]) new ArrayList<?>[10]; //OK,但是会有警告
List<?>[] list14 = new ArrayList<String>[10]; //编译错误,非法创建
List<?>[] list15 = new ArrayList<?>[10]; //OK
List<String>[] list6 = new ArrayList[10]; //OK,但是会有警告
*/
public static <T> T[] func1(T... params) {
return params;
}
public static <T> void func2(T[] arr) {
for (T t : arr) {
System.out.println(t);
}
}
public <T> T[] arrayWithTypeToken(Class<T> clazz, int size) {
return (T[]) Array.newInstance(clazz, size);
}