第一章 对象导论
一种纯粹的面向对象的程序设计的方式
- 万物皆对象 可以存储数据,也可以进行操作
- 程序是对象的合集,通过发送消息来告知彼此=动作
- 可以创建包含现有的对象包的方式来创建新的类型的对象
- 每个对象都是一个类的实例
- 一个特定的类型对象可以接受同样的消息
伴随多态的可相互交换对象
- 基类操作的都是泛化的对象
- 非面向对象编程的编译器产生的函数调用是前期绑定,OOP程序直到运行的时候才能确定代码的地址,使用的是后期绑定
- 为了执行后期绑定,Java 使用的特殊的代码方式来替代绝对地址调用
- Java的动态绑定是默认行为,不需要添加额外的关键字来实现
单继承的好处
- 生产效率
- 保证所有对象都具备一定的功能
- 垃圾回收更加方便
异常是一种对象,从出错的地方被抛出,专门设计用以 处理特定错误的异常处理器捕获;异常提供了一类从错误状况进行可靠恢复的途径
第二章 一切都是对象
Java的操作标识符实际上是对象的一个引用
基本数据类型
基本类型是变量直接存储值的,不需要创建引用
基本数据类型的大小
基本数据类型 | 大小 | 包装容器类型 |
---|---|---|
boolean | - | - |
char | 16bit | Character |
byte | 8bit | Byte |
short | 16bit | Short |
int | 32bit | Integer |
long | 64bit | Long |
float | 32bit | Float |
double | 64bit | Double |
void | - | - |
数组
数组对象创建一个引用数组,所有引用自动化初始为null,意为没有指定任何对象
创建一个存储基本数据类型的数组,编译器保证其基本的内存置零
类
当类的成员是的基本数据类型,没有进行初始化,编译器会保证其获取一个默认值,当变量作为类的基本成员使用的时候才能生效
public static void main(String[] args) {
Data data = new Data();
System.out.println(data.i);
System.out.println(data.d);
System.out.println(data.b);
}
static class Data {
private int i;
double d;
boolean b;
}
方法
方法只能通过类的一部分来创建,方法只能通过对象来实现调用,类必须能执行方法的调用
static关键字
公用一个存储空间,属于类
第三章 操作符
- 前缀运算会先执行的运算,再生成值; 后缀运算会先生成值,在执行运算
- 窄化转换容易丢失信息;不同数据类型的进行操作会发生提升的行为
第五章 初始化与清理
-
构造器是一类特殊类型的方法,没有返回值;new表达式返回的是对新建对象的引用,但是的构造器本身是没有的任何返回值的。
-
涉及基本类型的重载,参数的只能从一个较小的类型的自动提升到的一个较大的类型
this关键字
this关键字只能在方法在的内部使用,表示**“调用方法的那个对象的引用”**,如果在方法的内部调用同一个类的方法,就不必使用this
this关键字对于讲当前对象传递给其他的方法、 返回当前对象的引用时候十分有用
常见用法:就是在构造器里面调用构造器
注意事项:禁止在其他方法中调用构造器
一次只能使用一个this构造器,而且只能写在第一行,避免操作对象的时候对象还未创建成功。
进一步理解static关键字
static方法就是没有this的方法,static方法内部不能调用非静态方法。
this关键字就是当前对象的引用,static关键字修饰的方法是属于类,就类似C++里面的全局方法。因此方法被static修饰以后就可以访问其他的static域与方法。
finalize()方法
一旦垃圾回收器准备好释放对象占用的内存空间,将首先调用finalize()方法,并在下一次垃圾回收动作发生的时候,才会真正回收对象占用的内存
即:
- 对象可能不被垃圾回收
- 垃圾回收不等于析构
- 垃圾回收只与内存有关
因此finalize()方法主要是处理通过某种创建对象以外的方式的为对象分配了内存空间
成员初始化
- 初值是0,引用是null,静态一样
- 但是静态代码块只进行一次初始化
- 非静态实例初始化是在构造器之前执行的
- 数组的定义以后都是引用,初始化发生在创建新的对象以后
第六章 访问控制权限
“访问权限控制是具体实现的隐藏。把数据和方法包装进类中,以及具体实现的隐藏,通常称为封装。其结果是一个同时带有特征与行为的数据类型。
“出于两个原因,访问权限控制讲权限的边界划在了数据类型的内部。第一个原因是要设定客户端程序员可以使用和不可以使用的界限。第二个原因就是接口与具体实现的分离。如果结构是用以一组程序之中,而客户端程序员除了可以向接口发送信息以外什么都不做的话,那么就可以随意更改不是public的东西。”
类既不可以是private的也不可以是哦protected的。所以类的访问控制权限,只有包访问控制权限以及的public。
第七章 复用类
组合语法:将对象引用置于新类中即可
确保正确清理
“垃圾回收器可能永远无法被调用,即使被调用,它也可能以任何他想要的顺序来回收对象,最好的方法是除了内存以外,不能依赖回收器去做任何事,如果需要清理,最好是编写自己的清理方法,但不要使用finalize()”
组合与继承的关系
组合技术通常用于在新类中使用现有类的功能而非其接口的情况。即在新类中嵌入某个对象,让其实现所需要的功能,但新类的用户看到为新类定义的接口而非嵌入对象的接口。需要的在新类中嵌入一个现有类的private对象。
不推荐使用protected
向上转型
导出类转型成基类,继承图向上移动
final关键字
final关键字一般使用方式是:一个永不改变的编译时常量;一个在 运行时被初始化的值,而你不希望他被改变;
对于基本数据类型,final使数值恒定不变;对于对象引用,final使引用恒定不变(即引用一旦指向某个对象,无法指向另一个对象,但是对象自身是可以改变的)
空白final:
即允许声明没有具体值的域,但是必须的在每个构造器中对其进行赋值
final参数:
用来向匿名内部类传递数据
final方法:
使用的原因:
1.将方法锁定,防止任何继承类修改其含义
2.效率:与早起的内嵌调用有关系
类中的private方法隐式的都是final的
final类:
某个类被设计成final的,不允许任何人进行继承,且所有的方法隐式的指定为final的
初始化及类的加载:类在初次使用的时候才会进行加载,访问static域或方法的时候也会发生加载
第八章 多态
将一个方法与方法主体关联起来被称为绑定。若在程序的执行前进行绑定,称为前期绑定,是通过编译器和连接程序实现的;后期绑定是的运行时根据对象的类型进行绑定,也称为动态绑定或者运行时绑定。
Java内除了static方法与final方法外,其他所有的方法都是后期绑定
当导出类对象转换为基类引用的时候,所有域访问操作都会由编译器进行解析,因此不是多态的。
static class Super{
public int field = 0;
public int getField(){
return field;
}
}
static class Sub extends Super{
public int field = 1;
public int getField(){
return field;
}
}
public static void main(String[] args) {
Super sub = new Sub();
System.out.println("sub.field = "+ sub.field +",sub.getField() = " + sub.getField());
Sub sub2 = new Sub();
System.out.println("sub2.field = "+ sub2.field +",sub2.getField() = " + sub2.getField());
}
构造器调用顺序:
基类的构造器总是在导出类的构造过程中被调用,而且按照继承的层次逐渐向上链接,以使每个构造器都能得到调用。
static class Glyph {
void draw() {
System.out.println("Glyph");
}
Glyph() {
System.out.println("Glyph before");
draw();
System.out.println("Glyph after");
}
}
static class RoundGlyph extends Glyph {
private int radis = 0;
void draw() {
System.out.println("RoundGlyph,radis = " + radis);
}
RoundGlyph(int r) {
radis = r;
System.out.println("RoundGlyph,radis = " + radis);
}
}
public static void main(String[] args) {
new RoundGlyph(5);
}
这样做的原因:就是保证导出类具有基类的全部属性。
但是为啥调用中间会调用导出类的draw方法不用基类的draw方法?
JVM的new指令的执行过程:
- 首先的根据这个指令的参数定位常量池中类的符号引用,检查类的加载过程,如果没有需要执行类的加载过程
- 类加载完成以后根据Java堆的分配方式完成对象性内存的分配
- 将内存空间初始化置零
- 将必要的信息放在对象头内部
- 执行方法实行对象的初始化
具体实例化的过程就是在方法中将对象实例域相应的值设置到相应的空间内。方法中以调用父类的方法开始然后以自身的构造方法作为结束。
需要进一步的考虑一下。
用继承进行设计
准则:继承表达行为间的差异,并用字段表示状态上的变化
第九章 接口
接口和内部类为我们提供了一种将接口与实现分离的更加结构化的方法。
从一个抽象类继承,并想创建该新类的对象,需要为基类中所有的抽象方法提供方法定义,否则导出类也是抽象的
- 使用接口的核心原因:能够向上转型成多个基类型;防止客户端程序员创建该类的对象。
- 接口可以继承从而添加新的方法声明,同时也可以进行组合。
- 接口的继承是可以多继承的,但是要尽量避免
- 接口中的域自动都是static和final的
接口是实现多重继承的方式,生产遵循某个接口的的对象的典型方式就是工厂方法设计模式。
public class FactoryModel {
public static void serviceConsumer(ServiceFactory serviceFactory){
Service service = serviceFactory.getService();
service.method1();
service.method2();
}
public static void main(String[] args) {
serviceConsumer(new Implement1Factory());
}
}
interface Service{
void method1();
void method2();
}
interface ServiceFactory {
Service getService();
}
class Implement1 implements Service{
Implement1(){}
public void method1() {
System.out.println("Implement1 method1");
}
public void method2() {
System.out.println("Implement1 method2");
}
}
class Implement1Factory implements ServiceFactory{
public Service getService() {
return new Implement1();
}
}
第十章 内部类
- 将一个类定义放在另一个类的定义内部
- 内部类自动具有外围所有成员的访问权限
- 创建内部类,需要创建外部类的对象来创建内部类的对象。需要生成对外部对象类的引用,可以是有点.this以及.new
- private内部类提供了一个思路来完全阻止任何依赖于类型的编码,并且完全隐藏了实现细节
public class TestPirvateInnerClass {
public static void main(String[] args) {
Parcel parcel = new Parcel();
Contents contents = parcel.contents();
Destination fuck = parcel.destination("fuck");
}
}
class Parcel{
private class PContents implements Contents{
private int i = 11;
public int value() {
return i;
}
}
private class PDestination implements Destination{
private String lable;
public String readLabel() {
return lable;
}
private PDestination(String whereTo){
lable = whereTo;
}
}
public Destination destination(String s ){
return new PDestination(s);
}
public Contents contents(){
return new PContents();
}
}
public interface Contents {
int value();
}
public interface Destination {
String readLabel();
}
内部类的基础使用方法:
- 实现某个接口之后返回其引用
- 创建一个辅助类并将其隐藏
进一步的内部类的实现方式:
局部内部类:定义在方法中的类
public class Parcel5 {
public Destination destination(String s) {
class PDestinaion implements Destination {
private String label;
public String readLabel() {
return label;
}
private PDestinaion(String whereTo) {
label = whereTo;
}
}
return new PDestinaion(s);
}
public static void main(String[] args) {
Parcel5 parcel5 = new Parcel5();
Destination destination = parcel5.destination("fuck");
}
}
同理,内部类的方式可以嵌套在任何作用域内
匿名内部类:
public class Parcel7 {
//无参形式
public Contents contents(){
return new Contents() {
private int i = 11;
public int value() {
return i;
}
};
}
}
有参形式匿名内部类需要包含一个有参的构造函数
匿名内部类通过实例初始化可以实现构造器的效果
使用匿名内部类实现工厂模式
public class FactoryModel {
public static void serviceConsumer(ServiceFactory serviceFactory) {
Service service = serviceFactory.getService();
service.method1();
service.method2();
}
public static void main(String[] args) {
//普通实现
serviceConsumer(new Implement1Factory());
//匿名内部类实现
serviceConsumer(Implement2.factory);
}
}
interface Service {
void method1();
void method2();
}
interface ServiceFactory {
Service getService();
}
class Implement1 implements Service {
Implement1() {
}
public void method1() {
System.out.println("Implement1 method1");
}
public void method2() {
System.out.println("Implement1 method2");
}
}
class Implement2 implements Service {
private Implement2() {
}
public void method1() {
System.out.println("Implement2 method1");
}
public void method2() {
System.out.println("Implement2 method2");
}
public static ServiceFactory factory =
new ServiceFactory() {
public Service getService() {
return new Implement2();
}
};
}
class Implement1Factory implements ServiceFactory {
public Service getService() {
return new Implement1();
}
}
静态内部类
创建静态内部类的对象不需要访问外围类的对象,且不能从静态内部类的对象中访问非静态的外围类的对象
接口可以有内部类,接口内部类自动是static与public的
内部类的特性:
每个内部类都能独立地继承一个接口的实现,所以无论外围类是否已经继承某个接口的实现,对于内部内类来说都没有影响。
内部类完善了多重继承。
- 内部类可以有多个实例,每个实例都有自己的状态信息,并与外围类的对象信息相互独立
- 在单个外围类中,可以让多个为内部类以不同的方式现实同一个接口,或者继承自同一个类,
- 创建内部类对象的时刻不以来外围类对象的创建
- 内部歪没有“is-a”关系,是一个独立的实体
闭包
package innerclass;
interface Incrementable {
void increment();
}
class Callee1 implements Incrementable {
private int i = 0;
public void increment() {
i++;
System.out.println(i);
}
}
class MyIncrement {
public void increment() {
System.out.println("other operate");
}
static void f(MyIncrement myIncrement) {
myIncrement.increment();
}
}
class Callee2 extends MyIncrement {
private int i = 0;
@Override
public void increment() {
super.increment();
i++;
System.out.println(i);
}
private class Closure implements Incrementable{
public void increment() {
Callee2.this.increment();
}
}
Incrementable getCallbackReference(){
return new Closure();
}
}
class Caller {
private Incrementable callbackReference;
Caller(Incrementable c){
callbackReference = c;
}
void go(){
callbackReference.increment();
}
}
public class Callbacks {
public static void main(String[] args) {
Callee2 c2 = new Callee2();
Callee1 c1 = new Callee1();
MyIncrement.f(c2); //other operate
Caller caller1 = new Caller(c1);
Caller caller2 = new Caller(c2.getCallbackReference());
caller1.go();
caller1.go();
caller2.go();
caller2.go();
}
}
Caller2继承了MyIncrement就不能为了Incrementable的用途而覆盖increment方法,只能使用内部类来独立实现Incrementable;内部类Closure添加一个hook
内部类继承与覆盖
继承需要添加必要的引用,覆盖无用
匿名内部类与局部内部类的区别
匿名内部类只有一个构造器
第十一章 持有对象
foreach默认前向迭代器
第十二章 通过异常处理错误
异常是指阻止当前方法或者作用域继续执行的问题。异常抛出以后,Java会在堆上创建异常对象,然后当前执行被终止,从当前环境弹出对异常对象的引用。然后异常对象接管程序,开始寻找一个恰当的地方继续执行程序。这个恰当的地方就是异常处理程序。
RuntimeException
RuntimeException如果没有的被捕获而直达main(),那么程序退出前将调用异常的printStackTrace()方法。RuntimeException代表的是编程的错误,因此只能在代码中忽略RuntimeException异常及其类型,其他的异常的处理都是由编译器强制实行的。
finally
对于没有垃圾回收以及析构函数自动调用的语言来说,finally的作用主要是保证内存的释放。但是在java中需要讲内存以外资源恢复到初始的状态的时候,需要使用finally。
finally与return的关系
finally子句总会执行,所以在一个方法的内部,可以从多个点进行返回,并并且保证相关的清理工作可以执行
public class MultiReturn {
public static void f(int i){
try {
System.out.println("init");
System.out.println("1");
if (i == 1) return;
System.out.println("2");
if (i == 2) return;
System.out.println("3");
if (i == 3) return;
System.out.println("end");
return;
} finally {
System.out.println("clean");
}
}
public static void main(String[] args) {
for (int i = 1; i <=4; i++) {
f(i);
}
}
}
/*
init
1
clean
init
1
2
clean
init
1
2
3
clean
init
1
2
3
end
clean
*/
对于可能在构造阶段抛出异常,并要求清理的类,最安全的方式是使用嵌套的try-catch子句
即在创建需要清理的对象之后,立马进入一个try-catch语句快
异常匹配的方式是在按照代码的书写顺序找出最近的处理程序
第十三章 字符串
用于String类的“+”与“+=”是java中仅有的两个重载过的操作符,Java不允许程序员重载任何操作符
重写toString方法的时候,如果字符串的操作比较简单,可以信赖编译器,其会为你合理地构造最终的字符串结果。但是如果你在的toString方法里面用到了循环,建议使用StringBuilder对象来构造最终的结果。
格式化输出
格式化输出的抽象语法
%[argument_index$][flags][width][.precision]conversion
width控制一个域的最小尺寸 percision域的最大尺寸
Formattter类的类型转换
字符 | 含义 | 字符 | 含义 |
---|---|---|---|
d | 整数型 十进制 | e | 浮点数 科学计数 |
c | Unicode字符 | x | 整数十六进制 |
b | Boolean值 | h | 散列码 十六机制 |
s | String | % | 字符% |
f | 浮点数 |
Java5参考了C中的sprinftf()方法,已生成格式化的String对象。String.format()是一个static方法,接受与Formatter.format()一样的参数但是返回一个 String对象
正则表达式
\\d表示一位数字 (Java中双斜杠的表示正则表达式的反斜杠 插入一个普通的反斜杠需要四个斜杠)
字符类的正则匹配
B | 指定字符B |
---|---|
\xhh | 十六进制值为oxhh的字符 |
\uhhhh | 十六进制表示为oxhhhh的字符 |
\t | Tab |
\n | 换行 |
\r | 回车 |
\f | 换页 |
\e | 转义 |
字符类匹配方法
. | 任意字符 |
---|---|
[abc] | 包含abc和任意字符 |
[^abc] | 除了abc以外的任意字符 |
[a-zA-Z] | 范围 |
[abc[hij]] | 合并 |
[a-z&&[hij]] | 交 |
\s | 空白符空格 tab 换行 换页 回车 |
\S | 非空白字符 |
\d | 数字 |
\D | 非数字 |
\w | 词字符[a-zA-Z0-9] |
\W | 非词 |
边界匹配符号
^ | 一行的开始 |
---|---|
$ | 一行的结束 |
\b | 词的边界 |
\B | f非词的边界 |
\G | 前一个匹配结束 |
过去 Java对字符串操作支持相当不完善,不过随着几个版本的升级,可以看到java从其他语言中吸取了很多成熟的经验,现在对字符串的支持已经相当完善了
第十四章 类型信息
RTTI运行时类型识别 的原理:与类加载器有关系。所有类在第一次使用的时候,动态加载到JVM中。即程序创建第一个对静态成员的引用的时候,就会加载这个类(进一步说明构造器是类的静态方法,使用new创建新的对象的时候会当作对类的静态成员的引用)。根据java按需加载的机制,加载中会有验证字节码文件是否被破坏的验证操作。
获取加载类的信息Class.forName()
获取Class的引用Class.getClass()
类字面常量
使用.class创建对Class对象的引用的时候,并不会自动初始化Class对象
注册工厂
class Part {
public String toString() {
return getClass().getSimpleName();
}
static List<Factory<? extends Part>> partFactories = new ArrayList<Factory<? extends Part>>();
static {
partFactories.add(new FuelFiller.Factory());
}
public static Part createRandom(){
return partFactories.get(0).create();
}
}
class Fillter extends Part{}
class FuelFiller extends Fillter{
public static class Factory implements factory.Factory<FuelFiller>{
public FuelFiller create(){
return new FuelFiller();
}
}
}
class RegistedFactories {
public static void main(String[] args) {
System.out.println(Part.createRandom());
}
}
package factory;
/**
* @author Wildec
* @version 1.0
* @date 2021/3/1 10:45
*/
public interface Factory<T> {
T create();
}