Java 基础知识大纲
- 一、面向对象
- 二、类相关
- 三、重要关键字
- 四、内部类
- 五、抽象类 & 接口
- 六、编码
- 七、异常
- 八、注解
- 九、容器
- 十、内存区域
- 十一、垃圾回收
- 十二、类加载
- 十三、泛型
- 十四、反射
一、面向对象
1.1 对 Java 多态的理解
面向对象编程的三大特性:封装、继承、多态。
- 封装:隐藏类的内部实现机制。
- 继承:重用父类代码,为多态做铺垫。
- 多态:程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定。
实现多态的三个必要条件:继承、重写、向上转型。
- 继承:在多态中必须存在有继承关系的子类和父类。
- 重写:子类对父类中的某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
- 向上转型:将父类引用指向子类对象,只有这样,该引用才具备调用子类方法的能力。
实现形式:
- 基于继承实现的多态。
- 基于接口实现的多态。
1.2 父类静态方法能不能被子类重写
结论
父类的静态方法可以被子类继承,但是不能被子类重写。
当子类声明了一个与父类相同的静态方法时,只能称为隐藏。
- 父类
- 子类
- 示例。
- 运行结果。
二、Object 类相关
2.1 Java 中 ==、equals 和 hashCode 的区别
==
在中,分为基本数据类型和复合数据类型,基本数据类型包括、、、、、、、这八种。
- 对于基本数据类型,比较的是它们的值。
- 对于复合数据类型,比较的是它们在内存中的存放地址,即比较的是否是同一个对象。
equals
中默认的实现是比较两个对象是不是,和的效果是相同的。
而有些时候,对于两个不同的对象,我们又需要提供 逻辑 上是否相等的判断方法,这时候就需要重写方法。提供的某些类已经重写了方法,用于判断"相等"的逻辑,例如。
hashCode
- 对于相等两个对象,其返回的值一定相等
- 对于不同的对象要尽量做到不同。
在 Java&Android 基础知识梳理(8) - 容器类 中提到的的实现,替换的条件是判断:
假如我们只重写了方法,而没有重写方法,就会导致逻辑上相等的两个,放在了容器中的不同位置。
2.2 Integer
存储原理
- 属于基本数据类型,存储在栈中。
- 属于复合数据类型,引用存储在栈中,引用所指向的对象存储在堆中。
缺省值
泛型支持
泛型支持,不支持
int 与 Integer 之间的比较
- 非出来的与出来的不相等,前者指向存放它的常量池(数值位于到之间)或者堆,后者指向堆中的另外一块内存。
- 两个都是非出来的,如果在到之间,返回的是,否则返回的是,因为在编译的时候,会翻译成,而函数会对到之间的数进行缓存。
- 两个都是出来的,返回。
- 与相比,都为,因为会把自动拆箱为再去比较。
2.3 String
2.3.1 new String 和直接赋值的区别
和直接赋值的区别:
- ,可能创建一个或者不创建对象,如果这个字符串在常量池中已经存在了,那么直接指向这个常量池中的对象。
- ,至少创建一个对象。一定会在堆中创建一个中的对象,它的是,如果这个字符串在常量池中不存在,会在池中创建一个对象。
例子 1
只创建了一个对象,在编译器在编译时优化后,相当于直接定义了一个的字符串。
例子 2
和存储的是两个常量池中的对象,当执行时,首先会在堆中创建一个类,同时用指向的字符串对象完成初始化,然后调用方法完成对指向字符串的合并操作,接着调用的方法在堆中创建一个对象,最后将刚生成的对象的地址存放在局部变量中。
2.3.2 String、StringBuilder、StringBuffer 的区别
对比
- 中的是常量数组,只能被赋值一次。
- 在编译阶段就能够确定的字符串常量,没有必要创建对象,直接使用字符串常量的效率更高。
- 中的是一个很普通的数组,而且可以通过方法将新字符串加入到末尾,改变内容和大小。
- 允许多线程操作,其很多方法都被关键字修饰,而则不是,如果不考虑线程安全,应该是首选。
注意点
- 不停地创建对象是程序低效的原因,因此我们应该尽可能保证相同的字符串在堆中只创建一个对象。java基础知识梳理
- 当调用的时,如果常量池已经有了当前的值,那么返回这个常量指向的地址;如果没有,则将值加入到常量池中。
- String、StringBuffer、StringBuilder 详细对比
2.3.3 String 为什么要设计成不可变类
常量池的需要
字符串常量池是堆内存的一个特殊区域,当创建一个对象时,假如字符串已经存在于常量池中,则不会创建新的对象,而是直接引用已经存在的对象。
和指向常量池中的同一个对象,如果是可变类,对其的修改将会影响到。
HashCode 缓存的需要
因为字符串不可变,在创建的时候就被缓存,不需要重新计算。
多线程安全
由多个线程之间共享,不需要同步处理。
如何实现不可变
- 私有成员变量
- 的方法都是复制一份数据
- 是,因此不可继承
- 构造函数深拷贝,进行而不是直接将赋值给内部变量。
2.4 序列化 & 反序列化
Java&Android 基础知识梳理(2) - 序列化
三、重要关键字
3.1 final
可以用于以下四个地方:
- 变量:静态和非静态
- 定义方法的参数
- 定义方法
- 定义类
3.1.1 变量
静态变量
- 如果修饰的是一个基本类型,就表示这个变量被赋予的值是不可变的。
- 如果修饰的是一个对象,就表示这个变量被赋予的引用是不可变的。
非静态变量
被修饰的变量必须被初始化,初始化的方式有以下几种:
- 在定义的时候初始化
- 非静态变量在初始化块中初始化,不可在静态初始化块中初始化
- 静态变量可以在静态初始化块中初始化。
- 非静态变量可以在类的构造器中初始化,但是静态变量不可以
3.1.2 方法
不可以被子类重写,但是不影响被子类继承。
3.1.3 类
不允许被继承。
3.2 static
static 方法
- 静态方法不依赖于任何对象就可以访问,因此对于静态方法来说,是没有的。
- 静态方法中不能访问类的非静态成员变量和非静态成员方法。
static 变量
- 静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时被初始化。
- 非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。
- 成员变量的初始化顺序按照定义的顺序进行初始化。
static 代码块
- 块可以置于类的任何地方,类中可以有多个块。
- 在类初次被加载的时候,会按照块的顺序来执行每个块,并且只执行一次。
Java 中的 static 关键字解析
四、内部类
4.1 定义
内部类的定义:在一个外部类的内部再定义一个类。
4.2 分类
- 成员内部类:作为外部类的成员,可以直接使用外部类的所有成员和方法。
- 静态内部类:声明为的内部类,成员内部类不能有数据和方法,但嵌套内部类可以。
- 局部内部类:内部类定义在方法和作用域内。只在该方法或条件的作用域内才能使用,退出作用域后无法使用。
- 匿名内部类:匿名内部类有几个特点:不能加访问修饰符;当所在方法的形参需要被内部类里面使用时,该形参必须为。
4.3 作用
4.3.1 实现隐藏
外部顶级类即类名和文件名相同的只能使用和修饰,但内部类可以是、。
首先,定义内部类需要实现的接口:
再定义一个包装类:
由于我们将的实现类声明为了,因此外部并不知道它的存在,也就达到了隐藏的目的。
4.3.2 无条件地访问外部类当中的元素
这仅限于非静态内部类,它和静态内部类的区别是:
- 静态内部类没有指向外部的引用
- 在任何非静态内部类中,都不能有静态变量、静态方法或者静态内部类。
- 创建非静态内部类,必须要通过外部类来创建,例如;静态内部类则可以直接创建,
- 静态内部类只可以访问外部类的静态方法和静态变量。
4.3.3 实现多重继承
由于不允许多重继承,因此假如我们希望一个类同时具备其它两个类的功能时,就可以采用内部类来实现。
实现乘法的子类:
实现加法的子类:
- 通过内部类实现多重继承
4.3.4 避免修改接口而实现同一个类中两种同名方法的调用
用于解决下面的困境:一个需要继承另一个类,还要实现一个接口,而继承的类和接口里面有两个同名的方法。那么我们调用该方法的时候,究竟是父类的,还是实现的接口呢,这时候就可以使用内部类来解决这一问题。
- 需要继承的子类中有方法
- 需要实现的接口,同样有方法
- 采用内部类的方式避免出现困惑
- 调用方式
4.4 应用场景
幕后英雄的用武之地——浅谈 Java 内部类的四个应用场景
- 除了它的外部类,不再被其它的类使用
- 解决一些非面向对象的语句块
- 一些多算法场合
- 适当使用内部类,使得代码更加灵活和具有扩展性
4.5 内部类和闭包
闭包就是把函数以及变量包起来,使得变量的生存周期延长,闭包跟面向对象是一棵树上的两条枝,实现的功能是等价的。
涉及到闭包的两种内部是:局部内部类和匿名内部类。当它们引用外部变量时,外部的变量需要是的。
以下面这个例子为例,定义一个内部类的接口:
在编译之后,局部内部类会生成独立的文件,而变量是方法级别的,方法运行完变量就销毁了,而局部内部类对象还可能一直存在,不会随着方法运行结束就马上被销毁。这时候就会出现,局部内部类对象需要访问一个已经不存在的局部变量。
因此,通过将变量声明为,编译器会将局部变量复制一份,复制品作为局部内部类中的成员,这样,当局部内部类访问局部变量时,其实真正访问的是这个局部变量的复制品。
由于被修饰的变量赋值后不能再修改,所以就保证了复制品与原始变量的一致,就好像是局部变量的 生命期变长了,这就是的闭包。
匿名内部类为什么访问外部类局部变量必须是 final 的
五、抽象类 & 接口
5.1 区别
- 抽象类和接口都不能被实例化。
- 抽象类要被子类继承,接口要被类实现。
- 接口只能做方法的声明,抽象类可以做方法的声明,也可以做方法的实现。
- 接口里定义的变量只能是公共的静态常量,抽象类中的变量可以是普通变量。
- 抽象类里的抽象方法必须全部被子类实现;接口的接口方法必须全部被子类实现,否则只能为抽象类。
- 抽象类里可以没有抽象方法。
- 如果一个类里有抽象方法,那么这个类只能是抽象类。
- 抽象方法要被实现,所以不能是静态的,也不能是私有的。
- 接口可继承接口,并可多继承接口,但类只能单继承。
5.2 应用场景
抽象类
在既需要统一的接口,又需要实例变量或缺省方法的情况下,可以使用:
- 定义了一组接口,但又不想强迫每个实现类都必须实现所有的接口。
- 某些场合下,只靠纯粹的接口不能满足类与类之间的协调,还需要类中表示状态的变量来区别不同的关系。
- 规范了一组相互协调的方法,其中一些方法是共同的,与状态无关的,可以共享的,无需子类分别实现;而另一些方法却需要各个子类根据自己特定的状态来实现特定的功能。
接口
- 类与类之间需要特定的接口协调,而不在乎其如何实现。
- 需要将一组类视为单一的类,而调用者只通过接口来与这组类发生联系。
六、编码
6.1 为什么要编码
- 计算机中存储信息的最小单元是,所以能表示的字符范围是个。
- 要表示的符号太多,无法用一个字节来完全表示。
- 要解决这个矛盾必须要一个新的数据结构,从到必须编码。
6.2 编码方式
ASCII 码
码总共有个,用一个字节的低位表示。
ISO-8859-1
在码基础上制定了一系列标准来扩展编码,其仍然是单字节编码,总共能表示个字符。
GB2312
双字节编码,总的范围是,从是符号区,总共包含个符号;从是汉字区,包含个汉字。
GBK
扩展,加入更多的汉字,其编码范围是,和兼容。
GB18030
我国的强制标准,可能是单字节、双字节或者四字节编码,与兼容。
Unicode 编码集
试图创建一个全新的语言字典,将所有的语言互相翻译。在内存中 不需要编码格式,它只是一个字符串而已。只有当字符串需要在网络中传输或要被写入文件时,才需要编码格式。
-
具体定义了字符在计算机中的存取方法,它用两个字节表示转化格式。 -
的缺点在于很大部分字符仅用一个字节就可以表示,目前却需要使用两个,而采用了变长技术,不同类型的字符可以由个字节组成。- 如果一个字节,最高位为,表示这是一个字符。
- 如果一个字节,以开头,连续的个数表示这个字符的字节数。
- 如果一个字节,以开始,表示它不是首字节,需要向前查找才能得到当前字符的首字节。
6.3 对比
- 与编码规则类似,但是范围更大,它能处理所有汉字字符。
- 和都是处理编码,效率更高,它适合在本地磁盘和内存之间使用。
- 不是在网络之间传输,因为网络传输容易损坏字节流,更适合网络传输,对字符采用单字节存储,单字节损毁不会影响后面其它字符。
6.4 参考文章
- (1) Java 中的字符编码方式
- (2) Java 几种常见的编码格式
- Unicode, UTF-8, UTF-16
七、异常
中定义了许多异常类,并定义了作为所有异常的超类,将异常划分为两类和。
- :程序中无法处理的错误,例如、等,当此类错误发生时,将终止进程。
- :程序本身可以处理的异常。
- 运行时异常,及其子类,表示在运行时可能出现的错误,例如空指针、数组越界等,一般是由逻辑错误引起。
- 受检异常:除及其子类的异常。编译器会检查此类异常,并提示你处理本类异常 - 要么使用捕获,要么使用语句抛出,否则编译不通过。
八、注解
Java&Android 基础知识梳理(1) - 注解
九、容器
Java&Android 基础知识梳理(8) - 容器类
十、内存区域
Java&Android 基础知识梳理(3) - 内存区域
十一、垃圾回收
Java&Android 基础知识梳理(4) - 垃圾收集器与内存分配策略
十二、类加载
Java&Android 基础知识梳理(5) - 类加载&对象实例化
十三、泛型
Java & Android 基础知识梳理(12) - 泛型
十四、反射
版权声明:
本文来源网络,所有图片文章版权属于原作者,如有侵权,联系删除。
本文网址:https://www.bianchenghao6.com/h6javajc/4195.html