auto commit
This commit is contained in:
278
notes/Java 基础.md
278
notes/Java 基础.md
@ -13,7 +13,6 @@
|
||||
* [抽象类与接口](#抽象类与接口)
|
||||
* [super](#super)
|
||||
* [覆盖与重载](#覆盖与重载)
|
||||
* [内部类](#内部类)
|
||||
* [五、String](#五string)
|
||||
* [String, StringBuffer and StringBuilder](#string,-stringbuffer-and-stringbuilder)
|
||||
* [String 不可变的原因](#string-不可变的原因)
|
||||
@ -52,7 +51,7 @@ final A y = new A();
|
||||
y.a = 1;
|
||||
```
|
||||
|
||||
**2. 方法** </font> </br>
|
||||
**2. 方法**
|
||||
|
||||
声明方法不能被子类覆盖。
|
||||
|
||||
@ -80,7 +79,7 @@ public class A {
|
||||
|
||||
**2. 静态方法**
|
||||
|
||||
静态方法在类加载的时候就存在了,它不依赖于任何实例,所以 static 方法必须实现,也就是说它不能是抽象方法(abstract)。
|
||||
静态方法在类加载的时候就存在了,它不依赖于任何实例,所以静态方法必须有实现,也就是说它不能是抽象方法(abstract)。
|
||||
|
||||
**3. 静态语句块**
|
||||
|
||||
@ -88,11 +87,11 @@ public class A {
|
||||
|
||||
**4. 静态内部类**
|
||||
|
||||
内部类的一种,静态内部类不依赖外部类,且不能访问外部类的非 static 变量和方法。
|
||||
内部类的一种,静态内部类不依赖外部类,且不能访问外部类的非静态的变量和方法。
|
||||
|
||||
**5. 静态导包**
|
||||
|
||||
```source-java
|
||||
```java
|
||||
import static com.xxx.ClassName.*
|
||||
```
|
||||
|
||||
@ -132,12 +131,12 @@ public InitialOrderTest() {
|
||||
|
||||
存在继承的情况下,初始化顺序为:
|
||||
|
||||
1. 父类(静态变量、静态语句块)
|
||||
2. 子类(静态变量、静态语句块)
|
||||
3. 父类(实例变量、普通语句块)
|
||||
4. 父类(构造函数)
|
||||
5. 子类(实例变量、普通语句块)
|
||||
6. 子类(构造函数)
|
||||
- 父类(静态变量、静态语句块)
|
||||
- 子类(静态变量、静态语句块)
|
||||
- 父类(实例变量、普通语句块)
|
||||
- 父类(构造函数)
|
||||
- 子类(实例变量、普通语句块)
|
||||
- 子类(构造函数)
|
||||
|
||||
# 二、Object 通用方法
|
||||
|
||||
@ -192,15 +191,14 @@ x.equals(x); // true
|
||||
(二)对称性
|
||||
|
||||
```java
|
||||
x.equals(y) == y.equals(x) // true
|
||||
x.equals(y) == y.equals(x); // true
|
||||
```
|
||||
|
||||
(三)传递性
|
||||
|
||||
```java
|
||||
if(x.equals(y) && y.equals(z)) {
|
||||
if (x.equals(y) && y.equals(z))
|
||||
x.equals(z); // true;
|
||||
}
|
||||
```
|
||||
|
||||
(四)一致性
|
||||
@ -256,7 +254,7 @@ public class EqualExample {
|
||||
|
||||
hasCode() 返回散列值,而 equals() 是用来判断两个实例是否等价。等价的两个实例散列值一定要相同,但是散列值相同的两个实例不一定等价。
|
||||
|
||||
在覆盖 equals() 方法时应当总是覆盖 hashCode() 方法,保证相等的两个实例散列值也等价。
|
||||
在覆盖 equals() 方法时应当总是覆盖 hashCode() 方法,保证等价的两个实例散列值也相等。
|
||||
|
||||
下面的代码中,新建了两个等价的实例,并将它们添加到 HashSet 中。我们希望将这两个实例当成一样的,只在集合中添加一个实例,但是因为 EqualExample 没有实现 hasCode() 方法,因此这两个实例的散列值是不同的,最终导致集合添加了两个等价的实例。
|
||||
|
||||
@ -272,7 +270,7 @@ System.out.println(set.size()); // 2
|
||||
|
||||
理想的散列函数应当具有均匀性,即不相等的实例应当均匀分布到所有可能的散列值上。这就要求了散列函数要把所有域的值都考虑进来,可以将每个域都当成 R 进制的某一位,然后组成一个 R 进制的整数。R 一般取 31,因为它是一个奇素数,如果是偶数的话,当出现乘法溢出,信息就会丢失,因为与 2 相乘相当于向左移一位。
|
||||
|
||||
一个数与 31 相乘可以转换成移位和减法:31\*x == (x<<5)-x。
|
||||
一个数与 31 相乘可以转换成移位和减法:`31\*x == (x<<5)-x`,编译器会自动进行这个优化。
|
||||
|
||||
```java
|
||||
@Override
|
||||
@ -594,9 +592,9 @@ ac2.func1();
|
||||
|
||||
从 Java 8 开始,接口也可以拥有默认的方法实现,这是因为不支持默认方法的接口的维护成本太高了。在 Java 8 之前,如果一个接口想要添加新的方法,那么要修改所有实现了该接口的类。
|
||||
|
||||
接口也可以包含字段,并且这些字段隐式都是 static 和 final 的。
|
||||
接口的成员(字段 + 方法)默认都是 public 的,并且不允许定义为 private 或者 protected。
|
||||
|
||||
接口中的方法默认都是 public 的,并且不允许定义为 private 或者 protected。
|
||||
接口的字段默认都是 static 和 final 的。
|
||||
|
||||
```java
|
||||
public interface InterfaceExample {
|
||||
@ -607,7 +605,7 @@ public interface InterfaceExample {
|
||||
}
|
||||
|
||||
int x = 123;
|
||||
//int y; // Variable 'y' might not have been initialized
|
||||
// int y; // Variable 'y' might not have been initialized
|
||||
public int z = 0; // Modifier 'public' is redundant for interface fields
|
||||
// private int k = 0; // Modifier 'private' not allowed here
|
||||
// protected int l = 0; // Modifier 'protected' not allowed here
|
||||
@ -711,228 +709,6 @@ SuperExtendExample.func()
|
||||
|
||||
- 重载(Overload)存在于同一个类中,指一个方法与已经存在的方法名称上相同,但是参数类型、个数、顺序至少有一个不同。应该注意的是,返回值不同,其它都相同不算是重载。
|
||||
|
||||
## 内部类
|
||||
|
||||
在Java中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。
|
||||
为什么在Java中需要内部类?总结一下主要有以下四点:
|
||||
|
||||
1. 每个内部类都能独立的继承一个接口的实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。内部类使得多继承的解决方案变得完整,
|
||||
|
||||
2. 方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏。
|
||||
|
||||
3. 方便编写事件驱动程序
|
||||
|
||||
4. 方便编写线程代码
|
||||
|
||||
个人觉得第一点是最重要的原因之一,内部类的存在使得Java的多继承机制变得更加完善。在这之前,Java 的继承机制主要由接口和单根继承实现,通过实现多个接口里的方法,看似能够实现多继承,但是并不总是高效的,因为一旦我们继承了一个接口就必然要实现它内部定义的所有方法。现在我们可以通过内部类多次继承某个具体类或者接口,省去一些不必要的实现动作。
|
||||
|
||||
先来看一段示例代码来方便理解利用内部类完善多继承机制:
|
||||
|
||||
```java
|
||||
public class Father {
|
||||
public String powerFul = "市长";
|
||||
}
|
||||
|
||||
public class Mother {
|
||||
public String wealthy = "一百万";
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
public class Son {
|
||||
class Extends_Father extends Father{
|
||||
}
|
||||
|
||||
class Extends_Mother extends Mother{
|
||||
}
|
||||
|
||||
public void sayHello(){
|
||||
String father = new Extends_Father().powerFul;
|
||||
String mother = new Extends_Mother().wealthy;
|
||||
System.out.println("my father is:" + father + "my mother has:" + mother);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
显然,我们的 Son 类是不可能同时继承 Father 和 Mother 的,但是我们却可以通过在其内部定义内部类继承了 Father 和 Mother,必要的情况下,我们还能够重写继承而来的各个类的属性或者方法。
|
||||
这就是典型的一种通过内部类实现多继承的实现方式,但是同时你也会发现,单单从 Son 来外表看,你根本不知道它内部多继承了 Father 和 Mother,从而往往会给我们带来一些错觉。所以你看,内部类并不绝对是一个好东西,它破坏了封装性,用的不好反而会适得其反,让你的程序一团糟,所以谨慎!
|
||||
|
||||
广泛意义上的内部类一般来说包括这四种:成员内部类、局部内部类、匿名内部类和静态内部类。下面就先来了解一下这四种内部类的用法。
|
||||
|
||||
**1. 成员内部类**
|
||||
|
||||
成员内部类即内部类当做外部类得一个普通成员。
|
||||
|
||||
成员内部类可以无条件访问外部类得所有成员属性和成员方法
|
||||
|
||||
```java
|
||||
class Circle {
|
||||
private double radius = 0;
|
||||
public static int count =1;
|
||||
public Circle(double radius) {
|
||||
this.radius = radius;
|
||||
}
|
||||
|
||||
class Draw { //内部类
|
||||
public void drawSahpe() {
|
||||
System.out.println(radius); //外部类的private成员
|
||||
System.out.println(count); //外部类的静态成员
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
不过要注意的是,当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即默认情况下访问的是成员内部类的成员。如果要访问外部类的同名成员,需要以下面的形式进行访问:
|
||||
|
||||
`外部类.this.成员方法`
|
||||
|
||||
`外部类.this.成员变量`
|
||||
|
||||
虽然成员内部类可以无条件地访问外部类的成员,而外部类想访问成员内部类的成员却不是这么随心所欲了。在外部类中如果要访问成员内部类的成员,必须先创建一个成员内部类的对象,再通过指向这个对象的引用来访问,由于成员内部类是依附于外部类而存在的,也就是说,如果要创建内部类的对象,前提是必须存在一个外部类的对象,方式如下所示:
|
||||
|
||||
```java
|
||||
public class Test {
|
||||
public static void main(String[] args) {
|
||||
//第一种方式:
|
||||
Outter outter = new Outter();
|
||||
Outter.Inner inner = outter.new Inner(); //必须通过Outter对象来创建
|
||||
|
||||
//第二种方式:
|
||||
Outter.Inner inner1 = outter.getInnerInstance();
|
||||
}
|
||||
}
|
||||
|
||||
class Outter {
|
||||
private Inner inner = null;
|
||||
public Outter() {
|
||||
|
||||
}
|
||||
|
||||
public Inner getInnerInstance() {
|
||||
if(inner == null)
|
||||
inner = new Inner();
|
||||
return inner;
|
||||
}
|
||||
|
||||
class Inner {
|
||||
public Inner() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**2. 局部内部类**
|
||||
|
||||
局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。
|
||||
|
||||
```java
|
||||
class People{
|
||||
public People() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class Man{
|
||||
public Man(){
|
||||
|
||||
}
|
||||
|
||||
public People getWoman(){
|
||||
class Woman extends People{ //局部内部类
|
||||
int age =0;
|
||||
}
|
||||
return new Woman();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
注意,局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及static修饰符的。
|
||||
|
||||
**3. 匿名内部类**
|
||||
|
||||
匿名内部类,顾名思义,是没有名字的类,那么既然它没有名字,自然也就无法显式的创建出其实例对象了,所以匿名内部类适合那种只使用一次的情境,匿名内部类是唯一一种没有构造器的类。
|
||||
|
||||
典型得匿名内部类使用例子如下:
|
||||
|
||||
```java
|
||||
public class Test{
|
||||
public static void main(String[] args){
|
||||
Object obj=new Object(){
|
||||
@Override
|
||||
public String toString(){
|
||||
return "hello world";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
上述代码效果等同于如下代码:
|
||||
|
||||
```java
|
||||
public class MyObj extends Object{
|
||||
@Override
|
||||
public String toString(){
|
||||
return "hello world";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
public static void main(String[] args){
|
||||
Object obj = new MyObj();
|
||||
}
|
||||
```
|
||||
|
||||
为了一个只使用一次的类而单独创建一个 .java 文件,是否有些浪费和繁琐?
|
||||
在我看来,匿名内部类最大的好处就在于能够简化代码块。
|
||||
|
||||
匿名内部类往往是对某个父类或者接口的继承与实现,匿名类的基本使用语法格式如下:
|
||||
|
||||
```java
|
||||
new 父类/接口{
|
||||
//匿名类的实现
|
||||
}
|
||||
```
|
||||
常用场景如自定义比较器实现规则排序:
|
||||
|
||||
```java
|
||||
Arrays.sort(data, new Comparator<Integer>(){
|
||||
public int compare(Integer a,Integer b)
|
||||
{
|
||||
return (a+""+b).compareTo(b+""+a);//构建ab的比较器
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
**4. 静态内部类**
|
||||
|
||||
静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static。静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似,并且它不能使用外部类的非static成员变量或者方法,这点很好理解,因为在没有外部类的对象的情况下,可以创建静态内部类的对象,如果允许访问外部类的非static成员就会产生矛盾,因为外部类的非 static 成员必须依附于具体的对象。
|
||||
|
||||
常见使用场景如,静态内部类实现单例模式:
|
||||
|
||||
```java
|
||||
/**
|
||||
* 懒汉式单例类线程安全可可能有问题,性能会受到影响
|
||||
* 饿汉式不能实现延迟加载,不管将来用不用始终占据内存
|
||||
* 内部类的实现方式可以很好的解决上面的缺点
|
||||
*/
|
||||
public class InnerClassSingleton {
|
||||
private static class SingletonHolder{
|
||||
private static final InnerClassSingleton SINGLETON=new InnerClassSingleton();
|
||||
}
|
||||
private InnerClassSingleton() {}
|
||||
public static final InnerClassSingleton getSingleton(){
|
||||
return SingletonHolder.SINGLETON;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
此外,我们 Integer 类中的 IntegerCache 就是一个静态的内部类,它不需要访问外围类中任何成员,却通过内部定义的一些属性和方法为外围类提供缓存服务。
|
||||
|
||||
> [Java内部类详解](https://www.cnblogs.com/latter/p/5665015.html)
|
||||
|
||||
# 五、String
|
||||
|
||||
## String, StringBuffer and StringBuilder
|
||||
@ -1115,15 +891,15 @@ switch (s) {
|
||||
switch 不支持 long,是因为 swicth 的设计初衷是为那些只需要对少数的几个值进行等值判断,如果值过于复杂,那么还是用 if 比较合适。
|
||||
|
||||
```java
|
||||
// long x = 111;
|
||||
// switch (x) { // Incompatible types. Found: 'long', required: 'char, byte, short, int, Character, Byte, Short, Integer, String, or an enum'
|
||||
// case 111:
|
||||
// System.out.println(111);
|
||||
// break;
|
||||
// case 222:
|
||||
// System.out.println(222);
|
||||
// break;
|
||||
// }
|
||||
// long x = 111;
|
||||
// switch (x) { // Incompatible types. Found: 'long', required: 'char, byte, short, int, Character, Byte, Short, Integer, String, or an enum'
|
||||
// case 111:
|
||||
// System.out.println(111);
|
||||
// break;
|
||||
// case 222:
|
||||
// System.out.println(222);
|
||||
// break;
|
||||
// }
|
||||
```
|
||||
|
||||
> [Why can't your switch statement data type be long, Java?](https://stackoverflow.com/questions/2676210/why-cant-your-switch-statement-data-type-be-long-java)
|
||||
@ -1182,7 +958,7 @@ public class Box<T> {
|
||||
}
|
||||
```
|
||||
|
||||
> [Java 泛型详解](https://www.ziwenxie.site/2017/03/01/java-generic/)</br>[10 道 Java 泛型面试题](https://cloud.tencent.com/developer/article/1033693)
|
||||
> [Java 泛型详解](http://www.importnew.com/24029.html)</br>[10 道 Java 泛型面试题](https://cloud.tencent.com/developer/article/1033693)
|
||||
|
||||
# 十、注解
|
||||
|
||||
|
Reference in New Issue
Block a user