Java基础(含思维导图)

时间:2020-12-03 14:43:50

 很早之前整理的Java基础的一些知识点,思维导图:

  Java基础(含思维导图)

1、'别名现象'

对一个对象赋值另一个对象,会指向新的对象引用,赋值前的对象引用会由于不再被引用而被gc回收;

而基本类型则不同。基本类型存储了实际的值,而并非指向一个对象的引用,为其赋值,是将一个地方的内容复制到了另一个地方。

2、== 与 equals

对于==,如果作用于基本数据类型的变量,则直接比较其存储的 “值”是否相等;如果作用于引用类型的变量,则比较的是所指向的对象的地址;

  对于equals方法(注意:equals方法不能作用于基本数据类型的变量),如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;诸如String、Date等类对equals方法进行了重写,则比较的是所指向的对象的内容。

*重写equals方法,有2种方法实现:

  • 如果使用Eclipse,在自定义类右击——Source——Generate hashCode() and equals(),选择该类相应的成员作为标准,即可生成equals()和hashCode()(hashCode()和equals()是成对出现的,一定要同时重写,同时要重写hashCode()是可以保证对象的功能兼容于hash集合。这是一个好习惯,即使这些对象不会被存储在hash集合中);
  • 自定义equals(),根据具体业务需要,编写判重标准(注意:因为是重写,equals()的形参一定保持为Object类型,否则重写无效,可以用 @override 来修饰重写方法,如果重写不合法,编译时会报错)。

3、方法重载与覆写

重载:方法名相同而参数不同;

覆写:@override(一般为子类覆写父类)

区别点

重载方法

重写方法

参数列表

必须修改

一定不能修改

返回类型

可以修改

一定不能修改

异常

可以修改

可以减少或删除,一定不能抛出新的或者更广的异常

访问

可以修改

一定不能做更严格的限制(可以降低限制)

4、初始化

(1)先静态对象,后非静态对象;

(2)变量,构造器,方法;

(3)在初始化继承父类的子类时,先初始化父类,子类的静态对象;然后按照(1),(2)。

5、访问权限

(1)public: 用public修饰的类、类属变量及方法,包内及包外的任何类(包括子类和普通类)均可以访问;

(2)protected: 用protected修饰的类、类属变量及方法,包内的任何类及包外那些继承了该类的子类才能访问(此处稍后解释),protected重点突出继承;

(3)default: 如果一个类、类属变量及方法没有用任何修饰符(即没有用public、protected及private中任何一种修饰),则其访问权限为default(默认访问权限)。默认访问权限的类、类属变量及方法,包内的任何类(包括继承了此类的子类)都可以访问它,而对于包外的任何类都不能访问它(包括包外继承了此类的子类)。default重点突出包;

(4)private: 用private修饰的类、类属变量及方法,只有本类可以访问,而包内包外的任何类均不能访问它。

 

同一个类

同一个包

不同包的子类

不同包的非子类

public

Y

Y

Y

Y

protected

Y

Y

Y

N

default

Y

Y

N

N

private

Y

N

N

N

6、复用类

(1)基类初始化在子类之前;

(2)调用基类带参数的构造器,需要使用关键字super(Obj ....);

(3)在导出类中也可以出现基类的private方法,只不过不是覆盖,而是生成了一个新的方法。

7、final

(1)数据

一个永不改变的编译时常量;

一个在运行时被初始化的值,而你不希望它被改变;

一个final 和static修饰的域只占据一段不能改变的存储空间;

对于基本类型,final使数值保持恒定不变;对于对象引用,final使引用恒定不变。

(2)方法的参数

参数被final修饰,便无法更改引用所指向的对象;

(3)方法

把方法锁定,防止行为变化或被覆盖;

提升效率-->编译器将针对该方法的所有调用转为内嵌调用;

注:类中的所有private方法都隐式地被指定为是final的。

(4)类

不被继承。

8、多态

(1)方法调用绑定

前期绑定:将一个方法调用与方法主体关联起来叫做绑定。若在程序执行前进行绑定(由编译器和连接程序实现),则为前期绑定。Java中只有static和final方法属于前期绑定。

后期绑定(动态绑定):运行时根据对象的类型进行绑定。因此,可以编写对基类的打交道的程序,这些对导出类都可以正常运行。

构造器的调用顺序:高层的基类一直到低层的导出类;声明顺序的成员;导出类构造器的主体。(编写构造器的一条准则:用尽可能 简单的方法使对象进入正常状态;尽量避免调用其它方法)。

向上转型:会丢失被转型类具体的类型信息,导出类扩展的方法不可用;

向下转型:需要进行类型检查(如 instance of..等等)。

9、接口与抽象类

抽象类:

(1) 包含了抽象方法的一个类叫作“抽象类”。如果一个类里包含了一个或多个抽象方法,类就必须指定成abstract(抽象);

(2) 抽象类中可以有方法的实现

(3) 抽象类不能被实例化(初学者很容易犯的错),如果被实例化,就会报错,编译无法通过。只有抽象类的非抽象子类可以创建对象。

(4) 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。

(5)抽象类中的抽象方法只是声明,不包含方法体,就是不给出方法的具体实现也就是方法的具体功能。

(6)构造方法,类方法(用static修饰的方法)不能声明为抽象方法。

(7)抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。

接口:

(1)interface关键词前public,默认包访问权限;接口的方法被实现时,实现类中该方法必须为public。

(2)接口中的任何域都是static和final的,权限为public。

(3)任何抽象性都应是由真正需求产生的。

抽象类和接口的区别

  • 1. 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
  • 2. 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
  • 3. 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
  • 4. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。

10、内部类

将一个类的定义放在另一个类或方法的内部。

(1)如果想从外部类的非静态方法之外的任意位置创建某个内部的对象,就必须具体指明这个对象的类型:OuterClassName.InnerClassName。

(2)内部类对象拥有外围类所有成员的访问权:当外围类创建一个内部类对象时,内部类对象会秘密捕获一个指向外围类对象的引用。

(3)当在内部类中需要生成对外部类对象的引用,可以采用这种方式:外部类名字.this。

(4)想创建某个外部类的内部类对象时,且在该内部类不是静态的,先创建外部类对象a,然后 a.new InnerClassName()。

10.1 在方法和作用域内创建内部类

使用场景:实现了某类型的接口,于是可以创建并返回对其的引用;创建一个辅助类来解决某个复杂的问题,且不希望这个类是公共可用的。

如果定义一个匿名内部类,并且希望它使用一个在其外部定义的对象,那么编译器会要求其参数引用是final的。

10.2 嵌套类

如果不需要内部类对象与其外围类对象之间有联系,可将内部类声明为static的。

每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类均无影响。内部类使得多重继承的解决方案变得完整。

11、异常

Throwable对象:Error(编译和系统错误)和Exception(可被抛出的异常)。

特例:RuntimeException。

try...catch...finally...与throw、throws。

12、字符串

String对象不可变。

(1)格式化输出:printf()、System.out.format()、Formatter类、String.format();

(2)StringBuilder非线程安全与StringBuffer线程安全;

13、类型信息

在运行时使用类型信息,就必须首先获得对恰当的Class对象的引用:

13.1 RTTI(Run-Time Type Identification)、Class对象,类型转换检查

(1)Class.forName("");

(2)泛化的Class引用:

Class intClass = int.class;

Class<Integer> genericIntClass = int.class;

Class<? extends Number> bounded = int.class;

bounded = double.class;

(3)instanceof(isInstance):保持类型的概念,“你是这个类(或者派生类)吗";

equals(==):比较实际的Class对象,未考虑继承。

13.2 反射

(1)RTTI与反射的区别

RTTI:编译器在编译时打开和检查.class文件;

反射:.class文件在编译时是不可获取的,在运行时打开和检查.class文件;

(2)Filed、Method、Constructor类;

(3)看起来没有任何方式可以阻止反射到达并调用那些非公共访问权限的方法。对于域来说,的确如此,即便是private域。

13.3 动态代理

代理是为了提供额外或者不同的操作,而插入的用来代理"实际"对象的对象,充当着"中间人"的角色。

通过静态方法Proxy.newProxyInstance()创建动态代理,此方法需传入三个参数:类加载器,代理实现的接口(不是类或者抽象类),InvocationHandler接口的一个实现。动态代理可以将所有调用重定向到调用处理器,因此通常会向调用处理器o构造器传递一个"实际"对象的引用,从而使得调用处理器在执行其中介任务时,可将请求转发。

14、泛型(********)

泛化主要目的之一就是用来指定容器要持有什么类型的对象,并由编译器来保证类型的正确性。

可以创建一个元祖类库在一个方法调用中返回多个对象。

14.1 泛型方法

  • 所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前(在下面例子中的<E>)。
  • 每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
  • 类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
  • 泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像int,double,char的等)。

public <T> void f(T x){}

加上可变参数:public <T> void f(T... args){}

...

泛型类

泛型类的声明和非泛型类的声明类似,除了在类名后面添加了类型参数声明部分。

和泛型方法一样,泛型类的类型参数声明部分也包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。因为他们接受一个或多个参数,这些类被称为参数化的类或参数化的类型。

类型通配符

1、类型通配符一般是使用?代替具体的类型参数。例如 List<?> 在逻辑上是List<String>,List<Integer> 等所有List<具体类型实参>的父类。

2、类型通配符上限通过形如List来定义,如此定义就是通配符泛型值接受Number及其下层子类类型。

3、类型通配符下限通过形如 List<? super Number>来定义,表示类型只能接受Number及其三层父类类型,如Objec类型的实例。

14.2 擦除

在编译时,具体类型被擦除。

后续。。。

15、数组

大小固定,效率最高的存储和随机访问对象序列方式。

生成新的对象数组,length是容量。

16、I/O系统

16.1 File

使用File类list文件时,通过使用内部类过滤出指定字符串结尾的文件,回调。。。(策略模式)。

16.2 输入、输出类图

      Java基础(含思维导图)

重导向标准 I O

Java 1.1 在 System 类中添加了特殊的方法,允许我们重新定向标准输入、输出以及错误 IO 流。此时要用到

下述简单的静态方法调用:

setIn(InputStream)

setOut(PrintStream)

setErr(PrintStream)

transient、序列化。

17、自动装箱/拆箱

示例:

int i0 = -128;

Integer i1 = -128;

Integer i2 = -128;

Integer i3 = -129;

Integer i4 = -129;

int i5 = -129;

Integer i6 = new Integer(-129);

Integer i7 = new Integer(-128);

Integer i8 = new Integer(-128);

Integer a = 1;

Integer b = 2;

Long g = 3L;

Long h = 2L;

// 当包装器类型进行“==”比较时,会调用Integer.valueOf自动装箱基本数据类型为包装器类型

System.out.println(i1 == i2); // -->true

// 当包装器类型和基本数据类型进行“==”比较时,包装器类型会自动拆箱为基本数据类型。

System.out.println(i1 == i0);// i7新的Integer对象 -->true

System.out.println(i1 == i7);// i7新的Integer对象,未boxing -->false

System.out.println(i0 == i7);// i7自动拆箱为基本数据类型 -->true

System.out.println(i8 == i7);// i7,i8新的Integer对象 未boxing -->false

System.out.println(i3 == i4);//超出Integer缓存范围(-128-127) -->false

System.out.println(i3 == i5);// i3自动拆箱为int类型 -->true

System.out.println(i3.equals(i4));//看源码 ,发现比较的是值 -->true

System.out.println(i3.equals(i6));//看源码 ,发现比较的是值 -->true

// 首先a+b触发自动拆箱后值为int型,自动装箱后为Integer型,然后g为Long型 -->false

System.out.println(g.equals(a+b));

// 首先a+h触发自动拆箱后值为long型,因为int型的a会自动转型为long型的g然后自动装箱后为Long型,

// 而g也为Long型 -->true

System.out.println(g.equals(a+h));

/**

* 当 “==”运算符的两个操作数都是包装器类型的引用,则是比较指向的是否是同一个对象,而如果其中有一个操作数是表达式(即包含算术运算)

* 则比较的是数值(即会触发自动拆箱的过程)另外,对于包装器类型,equals方法并不会进行类型转换

*/

18、克隆

克隆方法clone()

浅克隆与深克隆