Java 主函数: 格式固定 、 被JVM识别

image.png
image.png
image.png

命名规范: 包名、类名、变量名、常量名

包名: xxxyyyzzz
类名:XxxYyyZzz (大驼峰)
变量名、方法名: xxxYyyZzz (小驼峰)
常量名:XXX_YYY_ZZZ

整型变量bit和表示范围

字节byte = 8bit,表示范围 -27 ~ 27 -1
short = 2个byte, 表示范围 - 215 ~ 215 -1
int = 4个byte,表示范围 -231 ~ 231 -1
long = 8个byte, 表示范围 -263 ~ 263 -1

long类型变量必须以”l”或”L”结尾,为什么吗?

正确写法:long a = 2222L;

一个事实:jvm默认int类型创建整型,默认double类型创建浮点数。

  • 先看long a = 2222为什么编译通过?因为在jvm可以以int类型创建常量2222,也能创建以long类型的变量a, 之后再将该变量被指向了该常量, 期间该常量甚至强制类型转换为long类型。
  • 再看long a = 222222222222222222 为什么编译错误?因为jvm在以int类型来创建该常量时就编译失败了,根本谈不上再此之后的强制类型转换的过程。

注:基本数据类型和引用类型的变量存放在栈中,常量等存放在堆中。

浮点型常量

image.png

建议全使用utf-8编码,为什么?

utf-8是互联网最通用最广泛使用的一种unicode编码方式, 全互联网符号都能被唯一编码,所以不存在乱码问题。什么是乱码呢?比如早期的英语系国家使用的ascll编码方式,但它只能编码28个字符, 可是全世界各种符号可不止这么一点,所以符号并不是被唯一编码,当写入和读出时采用不同的编码时,这就导致了乱码的问题

基本类型的运算的特别点:byte、char、short做运算时,结果都为int类型,当操作数是浮点常量时,结果为double类型。

不仅如此,byte类型与byte类型的运算结果也是int类型。以上这些规定可以被理解为防止内存溢出。】

java的8种基本数据类型是哪些?引用数据类型是哪些?

8种基本数据类型 = 4个整型byte\short\int\long, 2个浮点型float\double, 1个字符型char ,1个布尔型boolean
引用数据类型 = 类、接口、数组等。

String可以和所有(含boolean)的基本数据类型做运算,且只能是连接符+。如下,注意符号”+”是做加法运算还是连接运算?

  1. // + 做加法
  2. a + 1 结果为int类型
  3. a + 1.0 结果为double类型
  4. 'a' + 'b' 结果为int类型
  5. // + 做连接
  6. "a" + 1 结果为String类型
  7. "a" + 1.0 结果为String类型
  8. "a" + 'b' 结果为String类型

进制的表示方式

二进制(binary) : 0b或0b开头
十进制(decimal)
八进制(octal):0开头
十六进制(hex):0x或0X开头, 从0~9以及A~F(或a~f)

关于模除(取模)的方法

”a mod n“ 的截断法取模(⚠️ 商向零取整):
image.png
应用举例,8位无符号整数的值的范围是0到255.因此4+254将上溢出,结果是2。
因为258 mod 256 = 258 - 256*向零取整(258/256) = 258 - 256 = 2。

应用举例,8位有符号整数的值的范围,如果规定为−128到127,则126+125将上溢出,结果是−5。
因为 251 mod 256 = 251 - 256*向零取整(251/256) = 251 - 256 = -5。

补码是什么?计算底层都以补码的方式来存储数据, 又是为什么吗?。

正数和0的补码就是该数字本身。负数的补码 = 反码+ 1(换言之,负数的补码 = 对应正数的按位取反 + 1)
为什么需要补吗? 一是因为在计算机的数字电路中只有加法器,没有所谓的“减法器”。二是因为加法可以实现减法。
为什么加法可以实现减法?
这是建立在数值表示范围的条件,因为会溢出,所以在模256下的加减法,用0, 1, 2,…, 254,255表示其值,或者用−128, −127,…, −1, 0, 1, 2,…,127是完全等价的,只是两者最终结果有128的偏移而已。如何理解呢?

  • 先举个例子来理解”加法实现减法“:又一个12小时的圆形时钟⏰,不管在任何时刻,都满足:前进x小时=后退12-x小时,后退x小时=前进12-x小时,换句话说就是+x = -(12-x), -x = +(12-x)。 从其中的-x = +(12-x) > 0 就可以知道减法是可以由加法实现的,也就是说把-x和+(12-x)互换后对结果没有任何影响。
  • 结论:模256的加减法,不管是何种数值范围,始终都有“减x = 加 (256 减 x) ”, 其中(256 减 x) > 0。 我们可以发现,减法运算可以被替换为加法运算。所以原码的运算可以转化为补码运算,而得到原码的结果,只需要将补码下运算的结果偏移到原码即可。
    • 那么补码怎么具体来的?
      • 根据上面的解释,可以把−128到−1等价到高位二进制的1000 0000到1111 1111 ,0到127等价到低位二进制0000 0000到0111 11110。所以有符号数的加减法可以转为无符号的加法器来计算。这种解释的结果其实就是补码。注意,在计算上无符号二进制,但是认知上把高位1视为负数,0视为正数。

image.png

image.png
答:-69

关于异或

image.png
image.png

+1和自增1有区别? 答:自增1不会改变本身的类型。

s = s + 1 中的 1 和 s+=1 中的1是不同的概念,前者做加法运算(1的类型为int),后者叫自增(1的类型同自身)。
同理 s++; ++s; s+=1 ;s+=2; 等是自增操作,这种操作不会改变原类型,要区别于例如s = s + 1的加法运算。
image.png

& | ^ 到底是逻辑运算符吗?还是位运算符吗?

Java中:& | ^在两者中都用有,逻辑运算符的操作单位只能是boolean类型。位运算符的操作单位是二进制数字。

交换两个变量的值?(要求不能使用辅助空间)

方式一:(有缺点) 位运算符(异或) 结论: **b ^ a ^ a == a ^ b ^ a == b**

  1. int a = 2;
  2. int b = 3;
  3. a = a ^ b; // 新a = a ^ b
  4. b = a ^ b; // 新b = 新a ^ b = a ^ b ^ b = a
  5. a = a ^ b; // 新新a = 新a ^ 新b = a ^ b ^ a = b

缺点:只能适用数值类型。

方式二(有缺点)

  1. int a = 2;
  2. int b = 3;
  3. a = a + b; // 新a = a + b
  4. b = a - b; // 新b = 新a - b = ... = (a + b) - b = a
  5. a = a - b; // 新新a = 新a - 新b = ... = (a + b) - a = b

缺点1: 可能溢出; 缺点2: 只适用数值类型。

类似下面思路:
甲在左边,乙在右边,问甲乙怎么交换位置呢?答:甲先跳到乙头上,然后乙带着甲一起跳到左边;甲再从乙头上跳到右边。
image.png

三元运算符的三个细节

  • 细节1:表达式的结果必须兼容

    int和double是兼容类型,编译正确。 int和字符串不兼容,编译错误。

image.png

  • 细节2: 允许嵌套。

image.png

  • 细节3: 三元运算完全可以被if-else代替。反之则不成立。

运算符优先级(不用记)

  • (仅记下面几条吧)
    • 单目运算符只有一个操作数,双目运算符有两个操作数,三目运算符则有三个操作数。
    • image.png
    • && 优先级高于 ||
    • 布尔运算 优先于 位运算符 优先于 逻辑运算符。

image.png

⭐️数组的默认初始值(数值元素):char型为0,boolea型为false,引用型为null;

image.png

  • “数组元素是引用类型”举例: image.png

JVM - 内存的简化结构 (跳转到完整版

image.png

  • 局部变量:放在方法中的变量都是局部变量。
  • 引用类型:即new出来的结构:对象实例(类、数组、接口)。
    • 特别地: int[] arr = {1,2,3}; 这里的数组不是new出来的呀?
      • 答:其实是new出来的,只是因为jvm的推断机制下的简化写法,其标准写法是int[] arr = new int[3]{1,2,3}。同理,int[] arr = new int[]{1,2,3}, 这里int[]部分也使用了推断机制。
  • 堆:new 一个对象(或数组)时,堆中开辟出一段连续的内存。

一维数组的内存解析(引用、垃圾回收机制)

image.png

注意:相同类型

image.png

以上不完全正确:其中“刘德华”和“张学友”是常量,它们在常量池中,而非堆中。

  • 栈和堆之间的联系: 栈中局部变量的值 = 堆中对象(或数组)的首地址值。
  • 引用计数算法(堆中的垃圾回收机制): 对栈中的每个对象或数组的被引用进行计数,从而实现垃圾回收机制。
    1. - 当函数开始后,对应着局部变量的入栈,也伴随着一些局部变量对堆中对象或数组的引用。
    2. - 而当函数结束时,局部变量也出栈了,伴随着堆中对象或数组的引用计数减小。
    3. - 当某个对象或数值的引用计数为0时,稍后被JVM回收其空间。

    二维及多维数组的理解。int[][] arr; int[] arr[]; int arr[][];

    image.png

    所以,下面写法等价:

    • int[][] arr = new int[3][]; (主张写法)
    • int arr[][] = new int[3][];
    • int[] arr[] = new int[3][];

image.png
image.png

二维数组中“各个空间的值”是什么?比如[I@15db9742

image.png

  • arr[0]值是 [I@15db9742 :
    • [ 表示指向一维数组。
    • I 表示指向的是Int类型的数组。
    • 15db9742 表示指向数组的首地址。
  • arr[0][0]值是常量0
  • arr值是 [[I@6d06d69c :
    • [[表示指向二维数组

常用api

image.png

数组常见异常 : 1. 索引越界异常 2. 空指针异常

空指针的3种情况:
image.png

空指针:访问地址值为null,一般情况都是局部变量的值为null。注:null是引用数据类型的默认值。

面向对象的3条知识主线

image.png

⭐️面向过程 与 面向对象 的思想?POP考虑怎么做,OOP考虑谁来做。

image.png

  • “人把大象装进冰箱” 面向过程。(考虑怎么做)

image.png

  • “人把大象装进冰箱” 面向对象。(考虑谁来做)

image.png

image.png

OOP完整d1类结构:属性、构造器、方法、代码块、内部类

image.png

⭐️JVM内存解析(完整版)

  • 内存解析之前。

image.png

  • 内存解析的过程。(参考书《JVM规范》)

image.png

  • 说明

本地方法栈:是留给本地的其他语言的局部变量,比如C等。
虚拟机栈:是我们通常简称的栈,它主要存储Java方法中的局部变量。
堆:new 出来的对象实例(类、数组、接口),会在堆中开辟出一段连续的内存。
方法区:这里的常量和静态变量,在方法区内部又划分出了所谓常量池和静态域。 注:静态变量比如static String a; ,静态变量可以修改,特性是属于类变量,随着类加载而加载,不需要实例化即可调用。
image.png

注意:“Tome”等常量其实在常量池中,而不在堆中。

  • 引用类型的数组

image.png

  • 下面是更具体的

image.png

⭐️类中:成员变量(属性) 与 局部变量 的区别?

相同点:定义格式、先声明后使用、对应作用域。
不同点:声明位置不同、权限修饰符不同、默认初始化值不同、内存加载位置不同。
image.png

  • 特别注意

image.png
image.png

理解“万事万物皆对象”:

image.png

  1. 下面举个例子:

    前端中的每个元素对应后台中的一个实例。

数据表中的这些列对应着类的多个属性,数据表的行对应类的多个实例。

image.png

谈方法1:方法重载: 同名方法,但形参列表不同即可。(注:无关方法返回)

简单说:同名且形参列表不同,就可以唯一表示方法入口。

image.png
image.png

谈方法2: 可变个数的形参:String[] a 等价 String ...a

image.png

  • 实参的区别(举例)
    • 旧方式,image.png
    • 新方式:
      • image.png
      • image.png
  • 注意:两种方式对jvm是等价的,所以两者不可共存,不可构成方法重载。

⭐️谈方法3: 方法参数的值传递机制(传递副本)。

image.png
注意:不同变量类型在值传递机制下的区别。见下,数组元素交换的错误案例:

  1. int main(){
  2. int[] arr = new int[]{1,2};
  3. swap(arr[0),arr[1]); // 错误写法,由于值传递机制,所以并不能完成数组元素交换。
  4. }
  5. void swap(int a,int b){
  6. int tmp = a;
  7. a = b;
  8. b = tmp;
  9. }

方法4: 递归的方法。

image.png

封装与隐藏: 高内聚、低耦合; 4种访问权限修饰符

image.png

高内聚低耦合:该自己的事自己做,该帮助别人的事我才帮。

  • 修饰符权限范围 (助记:private类内,default包內,public工程內)

image.png

  • 修饰符的应用目标。

    image.png

构造器:一旦显式定义构造器,jvm就不再提供默认的空参构造器。

注意: 默认构造器的修饰符 与 类的修饰符 相同。

JavaBean: 符合特定标准的java类,好处:方便与其他“应用”交互数据。

image.png

疑问: 为何要无参的公共构造器? 答:除了new的方式实例化类,还有更为常见的反射方式,反射方式就需要空参构造器。

UML类图:+ getName(id:int) : String

image.png

类的this关键字的使用场景:this.局部变量this.方法this(...)来简化重载构造

  • this(…)来简化重载构造器。默认构造器始终要调用的,只不过有时可省略它,所以重载构造器更像是对默认构造器的封装。 注: 构造器的首行必须是this()或this(…)或super()或super(…),如果没有显式给出,那么就隐式调用默认构造器this()或super()。

    1. // 重载构造器不断复杂,可以使用this相互调用不同的构造器来简化。
    2. public class Stu(){
    3. public Stu(){}; // 默认构造器 等价于 this()。
    4. public Stu(String name){ // 构造器2
    5. // 该构造器没有显式给出this(...), 所以隐式调用了this()默认构造器。
    6. self.name = name;
    7. }
    8. public Stu(String name, int id){ // 构造器3
    9. this(name);
    10. self.id = id;
    11. }
    12. }

    package关键字:包是对工程中类的管理。用目录结构去理解package。

    举个例子: ```java // 要管理某java文件中的类, 可将该文件放在”com/xu/“目录下,并在该文件首行添加如下代码: package com.xu // 如此该文件中的类就被管理在“com.xu”包下了,便被其他文件所import。

// 其他代码

  1. <a name="BIejB"></a>
  2. ### ![image.png](https://cdn.nlark.com/yuque/0/2021/png/956564/1631597022719-1ff97493-938e-49e4-bfdd-b0688fb64d7e.png#clientId=u2e275823-d91a-4&from=paste&height=226&id=u1b136e68&margin=%5Bobject%20Object%5D&name=image.png&originHeight=451&originWidth=1047&originalType=binary&ratio=1&size=128731&status=done&style=none&taskId=u32eacf18-71b4-413e-a7f8-b946fe089e1&width=523.5)
  3. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/956564/1631598537162-a119f1cd-2376-4d40-adde-e4732032bab1.png#clientId=u2e275823-d91a-4&from=paste&height=140&id=u07e6b808&margin=%5Bobject%20Object%5D&name=image.png&originHeight=279&originWidth=944&originalType=binary&ratio=1&size=84811&status=done&style=none&taskId=u77943eb0-8f34-48ea-b8e3-5321c4c5ffb&width=472)
  4. - mvc的三种层次,从实现上就是特定的包名。
  5. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/956564/1631597061942-21d74c2a-39f3-481f-94ab-d4aed754ccb8.png#clientId=u2e275823-d91a-4&from=paste&height=268&id=uc18a337b&margin=%5Bobject%20Object%5D&name=image.png&originHeight=536&originWidth=994&originalType=binary&ratio=1&size=171883&status=done&style=none&taskId=u620b766b-c63f-4bb8-8399-a6492ac4a7c&width=497)
  6. <a name="yiUaM"></a>
  7. ### import关键字。1.“`java.lang`包”和“当前包”无需import。2. 不同包下的同名类有import冲突。3. import static
  8. - 处理不同包的同名类的import冲突。(完整包路径的类名来显式导入)
  9. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/956564/1631599845449-50288922-824b-4c22-8ce7-2d8107c0435e.png#clientId=u2e275823-d91a-4&from=paste&height=23&id=ua9381e9d&margin=%5Bobject%20Object%5D&name=image.png&originHeight=33&originWidth=1101&originalType=binary&ratio=1&size=52279&status=done&style=none&taskId=ud465b036-82ca-4c04-bdac-ebb853217c0&width=760.5)
  10. - import static 导入类或接口中静态结构。 注意:不是导入类,而是类中静态结构。举例:
  11. ```java
  12. package com.xu;
  13. public class Math{
  14. public static double pi = 3.141592;
  15. public static double circular_area(double r){
  16. return pi * r * r;
  17. }
  18. }

⭐️⭐️继承Interitance: public class Stu extends Person{ ... } 、private情况、单\多层继承、java.lang.Object类

  • ⭐️《Java编程思想》第129页说”继承不只是复制基类的接口,当创建了一个导出对象时,该对象包含了一个基类的子对象“。第137页说“组合和继承都允许在新的类中放置子类对象,组合是显式的这样做,而继承则是隐式的做”。依据《Java编程思想》所说,子类实例隐式地持有了一个基类实例,并且在子类实例中复制了基类接口(个人注:仅限非private的接口 ??),这样子类既可以调用自己声明的方法,又可以调用父类声明的方法们。
    • 补充:
      • 重写父类的方法:重写并不是说真正修改或覆盖了父类的方法,而是说子类调用自定义的同名同参方法而已。
      • 子类不能直接访问父类的private方法和属性的原因并不特殊,就是private的权限仅在父类实例的内部而已。通过父类的set()get()封装方法,子类依然可以间接“使用”它们。
      • 父类的实例先于子类创建。这点从子父类的构造器的使用先后顺序可说明。

image.png

  • java.lang.Object 公共父类

image.png

方法的重写: 1. private方法不可被重写 2. static方法不是重写。

  • 详细见继承
  • 子类实例的并没有复制父类实例的private接口(个人理解可能有误),所以谈不上被重写这一说法。
  • image.png: 假如父类方法返回值类似为A类,那么子类重写方法的返回值类型为A类或A类的子类。(基本类型的话,那就必须一致了。)

image.png

super: 基类实例的本身。this.idsuper.id有啥区别?、super调用构造器。

类继承后,this.idsuper.id有啥区别?
答:this.id时,先在子类实例中找id属性,找不到再去父类实例中找id属性。super.id时,只在父类实例中找id属性。
image.png

结论:子类和父类的默认构造器“this()和super()”两者都要被调用一次,如此开辟了子类实例和父类实例的内存空间。至于重载的构造器,无非是封装了默认构造器。

1 子类对象的实例化过程

子类对象使用构造器来实例化时,调用super(…)\super()等方法间接实例化了父类对象,同理,父类对象间接实例化间接父类对象,直到实例化了Object对象为主。
注意:虽然实例化了许多对象,但是外部看来只是一个对象(因为只暴露了子类对象的指针)。
image.png
image.png

  • image.png

下面是this()和super()解释:

  1. public class Man extends Person {
  2. public int id;
  3. // 默认构造。注意:下面三种构造是等价的, 不同程度上的省略。省略的前提是父类显式给出了默认构造器。
  4. // 第一种,省略所有默认构造器
  5. // 第二种,省略父类默认构造器
  6. public Man(){
  7. }
  8. // 第三种,显式地子父类默认构造器
  9. public Man(){
  10. super(); // 如果你要重载父类构造器的话,这里可以替换为super(10)等手段;
  11. }
  12. // 重载子类构造器,父类使用默认构造器
  13. public Man(int id){
  14. this(); // 这里面包括了super()
  15. this.id = id;
  16. }
  17. // 重载子类和父类构造器
  18. public Man(int id_child,int id_father){
  19. }
  20. }
  21. class Person{
  22. public int id;
  23. public Person(){};
  24. public Person(int id){
  25. this();
  26. this.id = id;
  27. };
  28. }

对象的多态性: 父类对象的多态性 = 子类重写父类方法 + 父类调用虚拟方法。

image.png

  • 多态的好处(个人理解): 简单说:父类多态性 = 子类重写父类方法 + 父类调用虚拟方法。—— 子类重写父类的方法是实现多态的基础,如果再让父类能够去调用指定(动态的)子类重写后的方法(即父类调用虚拟方法),那么就实现了父类的多态性。
    • 工程上讲多态易于API的维护和规范化。比如数据连接的例子,因为现实有mysql、sqlite等多种数据库,难道要为每个数据库都设计重载方法吗?并且不同数据库采用不同的API吗?其实不用,只要设计好的数据库该有的方法,具体地实现差异交给不同的子类们去重写这些方法,然后父类再使用子类重写好的方法即可。可见,数据库的例子中,多态机制可以允许规范的API,并屏蔽了不同子类的实现差异。:

image.png

  • 核心内容

    注意1: 多态的使用(虚拟方法的调用): 编译的时候调用了父类声明的方法,但是运行期,实际执行了子类重写父类的方法。 结论: 多态是运行时行为,而非编译时行为。如果说重载是静态绑定(早绑定),那么多态就是动态绑定(晚绑定)。 image.png

    注意2: 多态性不适用属性。对于属性,调用的一定是父类的属性。

image.png
image.png

区分重载和重写: 从多态的角度

image.png

子类父类的类型转化: 子类对象向上转型(多态),父类对象向下转型

image.png

java.lang.Object类 : 所有类的基类,它有许多通用的方法(值得一看)。

image.png

== 与 类的equals()方法 : 重写、toString()同理

  1. “==”是比较值,对于基本数据类型时,可能发生类型转换比如,1.0 == 1是被允许比较的,答案为true.
  2. java.lang.Object类中的equals(obj){this == obj}就是用”==”来比较,只不过比较的是对象的地址值。
  3. 但在一些特别的类,比如String、Data,重写equals()。因为更希望比较的是实体内容,而非对象的地址值。

toString()方法也同理,java.lang.Object类中的toString()原本是”类名@jvm虚拟地址”的输出,但在String、Data子类中被重写为实体内容的输出。

代码案例: 构造器重载、重写toString和equals

  1. package com.caffeflow.java;
  2. public class Person {
  3. public static void main(String[] args) {
  4. Person person = new Person(new String("xujia"), 27);
  5. Person person_2 = new Person(new String("quan"), 27);
  6. System.out.println(person.toString());
  7. System.out.println(person.equals(person_2));
  8. }
  9. private String name;
  10. private int age;
  11. private int weight;
  12. Person() { // 默认构造器
  13. };
  14. Person(String name, int age) {
  15. this(); // 调用默认构造器
  16. this.name = name;
  17. this.age = age;
  18. }
  19. Person(String name, int age, int weight) {
  20. this(name, age); // 调用其他构造器
  21. this.weight = weight;
  22. }
  23. @Override
  24. public String toString() { // 重写toString为实体内容返回
  25. return "super:" + super.toString() + " this:" + this.name + " " + age;
  26. }
  27. @Override
  28. public boolean equals(Object obj) { // 重写equals为实体内容比较
  29. if (this == obj) return true;
  30. if (obj instanceof Person) {
  31. Person p = (Person)obj; // 向下转型
  32. return p.age == this.age;
  33. }
  34. return false;
  35. }
  36. }

单元测试: import org.junit.Test @Test

注: 单元测试并非必须,且需要额外的包,可以暂时忽略。
第一步:确保在vscode中确保已经安装如下插件:
image.png
第二步:使用vscode创建一个java项目结构,并下载和导入包到java项目的referenced Libraries中。重启IDE。
https://github.com/junit-team/junit4/wiki/Download-and-Install 中下载如下两个文件:junit.jarhamcrest-core.jar
image.png
具体地,你可以手动将包放到lib目录中,然后确保settings.json中有如下阴影部分的配置项。
image.png
第三步:import org.junit.Test包,并对测试的方法上添加@Test。
单元测试: 如下代码,在vscode点击测试按钮,即可直接运行方法,而无需static main()方法。
image.png
image.png

包装类(对基本数据类型的包装): “基本数据类型、包装类与String类”的相互转换

image.png
image.png

自动装箱和自动拆箱(jdk5特性): 简写Integer a = 1 <—> Integer a = new Integer(1). 另外,对形参赋值、函数返回等也有效。

image.png

  • 自动装箱带来的好处

注意: java.util.Vector数组容器的addElement(Object obj)方法,只允许操作对象。

  1. import java.util.Vector;
  2. Vector v = new Vector();
  3. int a = 1;
  4. // jdk5之前
  5. v.add(new Integer(a));
  6. // jdk5之后
  7. v.add(a) // 自动装箱

image.png

static关键字 : 生命周期角度理解、类变量

希望无论是否创建对象或创建多少对象,某些特定数据在内存中只有一份,它被所有实例所共享。

  • 静态结构应该从生命周期的角度理解: 静态结构的生命周期同类本身,非静态结构的生命周期同实例本身。

image.png

  • 静态变量(类变量)随着类加载而加载,存放在方法区的静态域中。换言之:静态变量的加载早于对象实例的创建。静态方法同理。 => 静态结构不可调用非静态结构(静态结构不可使用this或super关键字)。
  • 什么时候声明为static? 比如Math.PI

image.png

单例设计模式:static实现

类的生命周期中只允许存在一个实例。场景举例: java.lang.runtime(运行时)、计数器、任务管理器、回收站、数据库连接池、

  • 有种实现方式

image.png

  1. // 懒汉式 (建议)
  2. class Order{
  3. private Order(){} // 关键1
  4. private static obj = null; // 关键2
  5. public static Order getObj(){ // 关键3
  6. if (obj == null)
  7. obj = new Order(); // 注意静态方法不可调用this关键字。
  8. return obj;
  9. }
  10. }
  11. // 饿汉
  12. class Order{
  13. private Order(){}; // 关键1
  14. private static obj = new Order(); // 关键2
  15. public static Order getObj(){ // 关键3
  16. return obj;
  17. }

类:代码块(初始化块): 只能用static修饰,即(非)静态代码块

  • 代码块不可以调用,加载即执行。具体地,静态代码块,随着类而加载并执行;非静态代码块,随着实例的创建而加载并执行。
  • 案例: JDBCUtils类。
  • 注意1:静态结构不可以调用非静态结构。
  • 注意2:调用顺序(与代码顺序无关):静态结构 > 非静态结构体 > 构造器 ```java public int a;

static { a = 1; } { a = 2; }

  1. <a name="WB3Tx"></a>
  2. ### 执行顺序: 任何静态结构类加载 > [非静态结构体 > 默认构造器 > 重载构造器]实例创建 。
  3. - 执行顺序:
  4. [父类静态结构体 > 子类静态结构体] ><br />[父类非静态结构体 > 父类类默认构造器 > 父类重载构造器] ><br />[子类非静态结构体 > 子类默认构造器 > 子类重载构造器]
  5. - 总结
  6. 父类静态结构 > 子类静态结构 > 父类实例创建 > 子类实例创建<br />实例创建:非静态结构 > 默认构造器 > 重载构造器
  7. ```java
  8. package com.caffeflow.java;
  9. public class son extends Father {
  10. public static void main(String[] args) {
  11. son obj = new son(1);
  12. }
  13. static {
  14. System.out.println("静态结构体");
  15. }
  16. {
  17. System.out.println("非静态结构体1");
  18. }
  19. public son() {
  20. super();
  21. System.out.println("默认构造器");
  22. }
  23. public son(int a) {
  24. this();
  25. System.out.println("重载构造器");
  26. }
  27. }
  28. class Father {
  29. public Father() {
  30. System.out.println("父类默认构造器");
  31. }
  32. public Father(int i) {
  33. System.out.println("父类重载构造器");
  34. }
  35. }

final关键字修饰:类->不可继承,方法->不可重写,变量->变为常量

变量分为:局部变量(方法内和形参)和属性

  • “final 属性”的赋值方式: 显式初始化、结构体、构造器、重载构造的形参
  • “final 局部变量”的赋值方式:显式初始化、方法的形参

static final: 仅可修饰属性和方法

  • static final 属性: 全局常量 (注:接口中常量均是static final修饰)
  • static final 方法

abstract关键字:有抽象方法的类一定要抽象类,反之抽象类不一定有抽象方法。

抽象类和抽象方法,必须要用abstract修饰。

image.png
image.png

abstract特殊用法:匿名子类

  1. // 抽象类
  2. public abstract Person{
  3. public abstract eat();
  4. public abstract breath();
  5. }
  6. // 匿名子类对象。 注意:Person是抽象类。
  7. Person p = new Person(){
  8. @Override
  9. public void eat(){};
  10. @Override
  11. public void breath(){};
  12. }

模版设计模式(TemplateMethod): abstract + 多态

image.png

  • 举个例子:计算某段代码执行时间的类。由于具体代码不确定,所以讲具体代码以抽象方法的形式暴露给子类实现。 下面代码中,子类讲实现code()抽象方法。

image.png

接口: 规范标准、多重继承的效果。

  • 接口: 接口是规范标准,而不是继承,但是可以实现(多)继承的效果。
  • 区分类和接口:比如u盘不是移动硬盘(不是继承关系),但它们都有存储功能(但有相似功能)。

image.png


image.png
image.png

  1. // 接口定义
  2. interfact A{
  3. public void fun1();
  4. public void fun2();
  5. }
  6. // 部分实现接口(抽象类)
  7. abstract class B implements A{
  8. public void fun1(){
  9. // 实现代码
  10. }
  11. }
  12. // 全部实现接口(类)
  13. class C implements A{
  14. public void fun1(){
  15. //实现代码
  16. }
  17. public void func2(){
  18. // 实现代码
  19. }
  20. }
  21. // 单继承与多继承接口的实现
  22. class CC{}
  23. interfact AA{}
  24. interfact BB{}
  25. class DD extends CC implements AA,BB{} //

接口有4种实现方式:(非)匿名实现类(非)匿名对象。注:和抽象方法如出一辙。

直接看下面代码即可了解“接口的匿名实现类及其匿名对象”

  1. public class App {
  2. public static void main(String[] args) throws Exception {
  3. // 代码可以看见,我实现了Human接口,并且实例化了实现类,且调用了实现的接口。
  4. // 但是匿名了接口的实现类,且匿名了实例化对象的名称。
  5. new Human() {
  6. @Override
  7. public void printName() {
  8. // TODO Auto-generated method stub
  9. System.out.println(name);
  10. }
  11. }.printName();
  12. }
  13. }
  14. interface Human {
  15. public String name = "xujia";
  16. public abstract void printName();
  17. }

image.png
image.png
image.png

接口的应用:代理模式、工厂模式

image.png
image.png

  1. public class App {
  2. /**
  3. * 在下面代码中有:
  4. *
  5. * 1. 网页数据类Page
  6. *
  7. * 2. 浏览器核心接口(是被代理的)BrowserCore
  8. *
  9. * 3. 浏览器核接口的实现类 BrowserCoreImpl
  10. *
  11. * 4. 浏览器客户端类(作为浏览器核心的代理类)
  12. *
  13. */
  14. public static void main(String[] args) throws Exception {
  15. // 完成代理类的实例化
  16. BrowserClient browserClient = new BrowserClient(new BrowserCoreImpl());
  17. // 使用代理类来完成打开网页的操作
  18. String url = "www.baidu.com";
  19. browserClient.open_page(url);
  20. }
  21. }
  22. class Page {
  23. public String url;
  24. public Page() {
  25. url = "404";
  26. }
  27. public Page(String url) {
  28. this.url = url;
  29. }
  30. public Object getHtml() {
  31. return this.url + ":html文本";
  32. }
  33. public Object getVideo() {
  34. return this.url + ":视频文件";
  35. }
  36. }
  37. interface BrowserCore {
  38. abstract public void open_html(Page page);
  39. abstract public void open_video(Page page);
  40. }
  41. class BrowserCoreImpl implements BrowserCore {
  42. @Override
  43. public void open_html(Page page) {
  44. // TODO Auto-generated method stub
  45. System.out.println("我是浏览器核心的实现类,已取回html数据,并打开:" + page.getHtml());
  46. }
  47. @Override
  48. public void open_video(Page page) {
  49. // TODO Auto-generated method stub
  50. System.out.println("我是浏览器核心的实现类:已取回video数据,并打开:" + page.getVideo());
  51. }
  52. }
  53. class BrowserClient implements BrowserCore {
  54. // BrowserClient是BrowserCore的代理类
  55. private BrowserCore browserCore;
  56. public BrowserClient(BrowserCore browserCore) {
  57. this.browserCore = browserCore;
  58. }
  59. public boolean checkUrl(String url) {
  60. if (url != "404")
  61. return true;
  62. else
  63. return false;
  64. }
  65. public void web_page_render() {
  66. System.out.println("我是浏览器客户端(代理类),我进行了网页渲染");
  67. }
  68. public void open_page(String url) {
  69. if (checkUrl(url)) { // 1. url检测
  70. System.out.println("我是浏览器客户端(代理类):url安全性检测通过");
  71. Page page = new Page(url); // 2. 拿到网页对象
  72. this.open_html(page); // 3.代理浏览器核心去打开html文件
  73. this.open_video(page); // 4. 同上,打开video文件
  74. this.web_page_render(); // 5. 进行了网页渲染
  75. } else
  76. System.out.println("我是浏览器客户端(代理类):url安全性检测失败");
  77. }
  78. @Override
  79. public void open_html(Page page) {
  80. this.browserCore.open_html(page);
  81. }
  82. @Override
  83. public void open_video(Page page) {
  84. this.browserCore.open_video(page);
  85. }
  86. }

image.png

image.png

工厂: 专门造对象的东西,目的是想把创建者和调用者分离。 下面是无工厂的代码,Clent01类同时作为创建者和调用者。 image.png 下面是简单工厂的代码,Client02类只是调用者,创建者由CarFactory类全权负责。注意:这里CarFactory是类,而非接口。 image.png 简单工厂缺点:违反开闭原则,即对扩展开放,对修改封闭。 比如上面代码,如果要新增”宝马车”,那么必须要修改CarFactory类,即修改工厂角色中的判断语句。 下面是工厂方法模式,简单工厂的缺点是由于工厂类的原因,那么可以由工厂接口来代替工厂类,同样是新增“宝马车”的情况下,只需要写一个实现接口的类即可。 image.png image.png 工厂模式同样有问题,还是违反了开闭原则,要么将接口类写死要么将接口的实现类写死。

其实可以真正实现了开闭原则。 原理:java的反射机制于配置文件的巧妙结合突破了这个限制—这在spring中体现的玲离尽致。

image.png

内部类(看懂即可,要求不高)

image.png

  • 内部类的2种实例化情况。

App.A a = new App().new A()
App.B b = new App.B();

  1. public class App {
  2. public static void main(String[] args) throws Exception {
  3. // 一般内部类实例化
  4. App app = new App();
  5. App.A a = app.new A(); // 或者A a = ...
  6. System.out.println(a.name);
  7. // 被static修饰的内部类的实例化
  8. App.B b = new App.B(); // 或者B b = ...
  9. System.out.println(b.name);
  10. }
  11. class A {
  12. public String name = "xiaomei";
  13. }
  14. static class B {
  15. public String name = "xiaoxu";
  16. }
  17. }


被内部类所使用的外部类的局部变量,必须要被 finnal 修饰。

如下代码中的int num即是被内部类所使用外部类的局部变量。
jdk7及之前必须显式写为final int num = 10,jdk8及之后省略final隐式写为int num = 10;
image.png

  • 一种解释:

从int num的作用域上讲,的确覆盖了内部类,但是从字节码文件上讲,外部类和内部类分别有各自的字节码文件,即从生命周期上讲,int num出了外部类的方法体就结束了,但是内部类的生命周期却与外部类一样长。总结来讲,int num的作用域覆盖了内部类,但是int num的生命周期却比内部类短,于是乎,将num的副本传递给了内部类使用,而非num本身,且为了保证该副本与num的一致性,便使用了final int num,让num成为”常量“。

`

`

异常处理

image.png

  • 编译时异常(或称受检异常) 、运行时异常

image.png
image.png

异常处理方式一:try-catch-finally : 1. 处理编译时异常、2. 注意异常类型的子父类关系、3. finally可选1,但存在则一定会被执行2 哪些语句需要放在finally中呢?3:见下。

finally存在则一定会被执行:
try-catch-finally中,首先执行try或者catch中的语句中的非return和throws部分,然后跳到finally中执行全部语句,最后再回到try或catch中去执行其return或throws部分(前提是finally中没有return等,否则直接退出)。

  1. @Test
  2. public void test_test2() {
  3. System.out.println(test2());
  4. }
  5. public int test2() {
  6. try {
  7. System.out.println('a');
  8. return 1;
  9. } catch (Exception e) {
  10. System.out.println('b');
  11. return 2;
  12. } finally {
  13. System.out.println('c');
  14. return 3;
  15. }
  16. }

以上结果为: a c 3

  1. @Test
  2. public void test_test2() {
  3. System.out.println(test2());
  4. }
  5. public int test2() {
  6. try {
  7. System.out.println('a');
  8. return 1;
  9. } catch (Exception e) {
  10. System.out.println('b');
  11. return 2;
  12. } finally {
  13. System.out.println('c');
  14. // return 3;
  15. }
  16. }

以上结果为:a c 1

image.png
image.png
image.png

  • finally:存在则必定执行 -比如: 资源关闭

image.png

异常方式二:throws + 异常类型、重写异常方法的规则、如何选择throws 或者 try-catch-finally ?

  • thows + 异常类型

简单说:不自己处理异常,只是将异常抛给方法的调用者,你可以不断地抛,但是一般最多抛到main方法为止,最终使用try-catch-finally来真正处理异常。

  • 重写异常方法的规则:

子类的异常类型 <= 父类的异常类型
image.png
image.png

手动抛出异常:比如:throw new Exception(“输入错误”)

以下3个要点

  1. import org.junit.Test;
  2. public class ExceptionTest {
  3. @Test
  4. public void test() {
  5. Student stu;
  6. // 要点3: 使用try-catch真正处理异常
  7. try {
  8. stu = new Student(-1);
  9. System.out.println(stu.id);
  10. } catch (Exception e) {
  11. System.out.print(e.getMessage());
  12. }
  13. }
  14. }
  15. class Student {
  16. public int id;
  17. // 要点2: 函数throws将异常继续往上抛
  18. public Student(int id) throws Exception {
  19. if (id < 0) {
  20. // 要点1: 手动抛出异常对象
  21. throw new Exception("ID不能为负数哦");
  22. }
  23. this.id = id;
  24. }
  25. }

自定义异常类

  1. class MyException extends Exception {
  2. static final long serialVersionUID = -3387511111124229948L;
  3. public MyException() {
  4. }
  5. public MyException(String msg) {
  6. super(msg);
  7. }
  8. }

异常综合练习与总结

对一个除法操作进行异常处理

  1. package 异常;
  2. import org.junit.Test;
  3. public class Exce {
  4. @Test
  5. public void test_div() {
  6. String[] ss = new String[] { "4","-1"};
  7. div(ss);
  8. }
  9. public void div(String[] args) {
  10. try {
  11. int a = Integer.parseInt(args[0]);
  12. int b = Integer.parseInt(args[1]);
  13. int res = ecm(a, b);
  14. System.out.print(res);
  15. } catch (ArrayIndexOutOfBoundsException e) {
  16. System.out.println("缺少参数");
  17. } catch (NumberFormatException e) {
  18. System.out.println("数据类型不一致");
  19. } catch (ArithmeticException e) {
  20. System.out.println("除0");
  21. } catch (EcDefException e) {
  22. System.out.println(e.getMessage());
  23. }
  24. }
  25. public int ecm(int a, int b) throws EcDefException {
  26. if (a < 0 || b < 0)
  27. throw new EcDefException("分子或分母为负");
  28. return a / b;
  29. }
  30. }
  31. class EcDefException extends Exception {
  32. // 自定义异常类
  33. static final long serialVersionUID = -338751699329948L;
  34. public EcDefException() {
  35. };
  36. public EcDefException(String msg) {
  37. super(msg);
  38. }
  39. }

image.png
image.png

一些面试题

image.png