java基础知识
Easul Lv6

相关链接

小技巧

  • 在写一个类或者方法之前先明确思路,可以按照下边的思路来想.
    • 需求:练习一个hello world程序
    • 思路:(阐述大体实现流程)
      1. 定义一个类
      2. 定义一个主函数,为了让类独立运行
      3. 用输出语句在控制台输出hello world字样
    • 步骤:(每一个想法具体怎么实现)
      1. 用class关键字完成类的定义.
      2. 用main函数写出主入口
      3. 用System.out.println输出相关语句
  • 进制转换用倒序取余法(用于10进制和其他进制转换)
  • 用Integer.MAX_VALUE可以获得int最大值
  • Java是强类型语言,if的判断条件只能是boolean
  • Java虚拟机在内存会默认开辟一定量空间,如果内存溢出,说明虚拟机开辟的空间不够,可以在开启虚拟机的时候修改配置,加大内存开辟空间
  • 开发的时候要提高代码的复用性
  • 数据多了可以用数组存起来,例如进制转换可以用数组存每一位.然后倒序输出.
  • java中没有方法嵌套.不能在一个方法中再写一个方法.

常见名词解析

折叠代码块YAML 复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
软件: 一系列按照特定顺序组织的计算机数据和指令的集合.
DOS: Disk Operate System(磁盘操作系统)
SUN: (Stanford University Network)斯坦福大学网络公司
Solaris: (SUN公司开发的系统)
JVM: (Java Virtual Machine)Java虚拟机,可以实现Java语言跨平台
JRE: (Java Runtime Environment)Java运行时环境,包括JVM和核心类库
JDK: (Java Development Kit)系统开发工具包,包括JRE和开发工具
解释: JDK装完之后里边已经有了JRE的运行环境了,下边再装JRE,就相当于另装一个运行环境.
SDK: (System Development Kit)系统开发工具包
JavaSE: 基础核心,用于面向对象,API,JVM
JavaME: 移动端,用于嵌入式设备,被android替代
JavaEE: 面向企业,用于JSP Servlet 五大框架
交互方式:
GUI: (Graphical User Interface) 图形化界面
CLI: (Command Line Interface) 命令行操作

JDK目录

折叠代码块YAML 复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
JDK: Java开发工具包,包含了jre(运行环境,有jvm和核心类库)
bin: (binary)存放可执行文件.java中的开发工具
javac: 编译工具(检查代码有没有错误,有错误报错,没错误编译,没有启动jvm)
java: 执行工具(启动jvm,执行某类,然后将类加载到内存执行)
jar: 打包工具
javadoc: 提取java文件中的文档注释,生成网页说明书
include: Java本地方法,调用C或C++底层文件
jre: Java运行环境.只用来运行环境
lib: 存放Java库文件(jar包)
rt.jar: Java运行时所需文件,包含了java程序常用的包,java.lang,java.util,java.io等
tools.jar: Java编译类时用到(javac)
dt.jar: Java的swing的包
src.zip: 存放了Java源码

CLASSPATH

  • 没有配置该环境变量,java默认在当前目录寻找运行的class文件
  • 配置后,java还会在CLASSPATH配置的目录中寻找相关class文件
  • CLASSPATH配置的目录加.会在当前目录找,不加也会在当前目录找,但需要其他路径加分号,加上.比较好,提高可阅读性.
  • dt.jar是swing的包,不用可以不加
  • tools.jar是编译时用的包,javac已经封装好该路径,也可以不用加

文档规范

折叠代码块YAML 复制代码
1
2
3
4
5
标识符: 就是变量名,可以用_和$来连接某些名称
注释: 可以用javadoc读取源码的文档注释生成源码说明书,javac编译的时候,注释不编译到字节码文件
文件名: 大驼峰
类名: 与文件名相同
main: 主程序入口

文档生成

  • 类必须为public
  • 只读取文档注释,单行多行注释不提取
  • 只提取publicprotected的成员信息,不提取private
  • 文档注释可以加@param@return
    折叠代码块BASH 复制代码
    1
    2
    3
    # -d 生成路径
    # 具体可以参考:https://blog.csdn.net/weixin_44170221/article/details/106502037
    javadoc -d ./Tool Tool.java

基本数据类型

  • Java的char两个字节,Java有8种基本数据类型,byte short int long float double char boolean
  • Java的char是使用unicode表
  • long型数据需要写l,float需要写f
  • Java每一个大括号都是一个作用域,if大括号定义的变量,外部无法读取到.
  • 类型不一致,内存空间就不一致.byte和int所占空间不同,运算的时候需要将小类型转换为大类型
    image
    折叠代码块JAVA 复制代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // 在编译的过程中先查看4是否在byte范围内,在的话将4强转为byte,然后赋值给b
    byte a = 4;

    // 最后会出错是因为后边两个数是变量,相加可能超过byte范围
    // 常量可以直接判断,变量因为会变所以不能判断
    // 故而直接变成两个数相加结果为int
    byte b1 = 4;
    byte b2 = 5;
    byte c = b1 + b2;

    // 这样就不报错了
    final byte b1 = 4;
    final byte b2 = 5;
    byte c = b1 + b2;

    // 相加时,byte自动转为int
    byte d = 1
    int d2 = 2
    int result = d + d2;

数据初值

  • 基本类型中char\u0000(空格),booleanfalse,其他都是0(\u表示unicode)
  • 引用类型(例如String)初值都是null
  • 计算机用补码存数据

变量

折叠代码块JAVA 复制代码
1
2
3
// 先声明,再赋值
int firstNumber;
firstNumber = 1;

常量

折叠代码块JAVA 复制代码
1
2
3
// 常量多个单词连接用下划线
// 下边只可以使用,不能再修改其值
final int NUMBER_FIRST = 1;

运算

  • 两个整数做除法还是整数,想要小数就给末尾加个.0
  • 异或(^): 看是不是不一样,不一样就是真
  • 与(&), 位或(|), 异或同样可以运算boolean值
  • &&和&,||和|
    • 都是左边的符合条件就阻断,右边的需要两边都使用(位运算符)
  • 程序的基本功能是处理数据

位运算符

  • 左移 <<,右移 >>,无符号右移 >>>,与 &,或 |,异或 ^,取反 ~
  • 位运算速度快

与&

特点

00101111 & 00001111(和1相与的地方可以取到原来数的位,与0相与则没有了)

作用

  • 10进制转换为其他进制
  • 取指定二进制位,可以用于二进制转到其他进制;

方式

  • 10进制转换为其他进制
    折叠代码块JAVA 复制代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    // 十进制,下边都是55
    int decimal = 55;
    // 二进制
    int binary = 0b110111;
    // 八进制
    int octal = 067;
    // 十六进制
    int hex = 0x37;

    // 十进制转二进制
    decimal = 55;
    StringBuilder result_1 = new StringBuilder();
    while (decimal != 0) {
    result_1.insert(0, String.valueOf(decimal & 1));
    decimal >>= 1;
    }
    System.out.println(result_1.insert(0, "0b").toString());

    // 十进制转八进制
    decimal = 55;
    StringBuilder result_2 = new StringBuilder();
    while (decimal != 0) {
    result_2.insert(0, String.valueOf(decimal & 7));
    decimal >>= 3;
    }
    System.out.println(result_2.insert(0, "0").toString());

    // 十进制转十六进制
    // 可以通过让一个数&15,取完第一个四位,然后右移第一个数,再与15
    decimal = 55;
    StringBuilder result_3 = new StringBuilder();
    while (decimal != 0) {
    result_3.insert(0, Integer.toHexString(decimal & 15));
    decimal >>= 4;
    }
    System.out.println(result_3.insert(0, "0x").toString());

    // 十进制转十六进制
    // 也可以通过让一个数&15,取完第一个四位,然后15左移.
    decimal = 55;
    StringBuilder result_4 = new StringBuilder();
    int number = 15;
    while (true) {
    result_4.insert(0, Integer.toHexString(decimal & number).charAt(0));
    if (number >= decimal) {
    break;
    }
    number <<= 4;
    }
    System.out.println(result_4.insert(0, "0x").toString());
  • 二进制转换其他进制
    折叠代码块JAVA 复制代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    // 转换8, 16进制,取3或4位,然后每一位乘2的幂次再加起来,然后右移
    // 转换10进制,每一位都乘2的幂次相加

    // 十进制,下边都是55
    int decimal = 55;
    // 二进制
    int binary = 0b110111;
    // 八进制
    int octal = 067;
    // 十六进制
    int hex = 0x37;

    // 二进制转十进制
    int result_1 = 0;
    int times = 0;
    binary = 0b110111;
    while (binary != 0) {
    result_1 += (binary & 1) * Math.pow(2, times++);
    binary >>= 1;
    }
    System.out.println(result_1);

    // 二进制转八进制
    StringBuilder result_2 = new StringBuilder();
    binary = 0b110111;
    while (binary != 0) {
    result_2.insert(0, String.valueOf(binary & 7));
    binary >>= 3;
    }
    System.out.println(result_2.insert(0, "0").toString());

    // 二进制转十六进制
    StringBuilder result_3 = new StringBuilder();
    binary = 0b110111;
    while (binary != 0) {
    result_3.insert(0, String.valueOf(binary & 15));
    binary >>= 4;
    }
    System.out.println(result_3.insert(0, "0x").toString());

或|

  • 特点:00101111 | 00001111(可以保留全部有效位,原来是1,肯定还是1)
  • 可以用于一个数低四位与高四位数据取出后交换其位置。(或操作,原来是什么,现在还是什么)
    折叠代码块JAVA 复制代码
    1
    2
    3
    4
    5
    6
    // 00100011 ->00110010,35->50
    int number = 35;
    int high = number & 240;
    int low = number & 15;
    int newNumber = (high >> 4) | (low << 4);
    System.out.println(newNumber);

异或^

特点

6 ^3 ^ 3=6(异或两次还是原来的数据)

作用

  • 可以用来简单加密
  • 用于不给其他变量,交换两个数的数据(int加减可能会超出数据范围,用异或就不会超出范围)

左移<<

左移几位相当于乘几个2(后边二进制补0)

右移>>

右移几位相当于除以几个2,只保留整数(前边二进制正数补0,负数补1)

无符号右移 >>>

原来的前边都补0

赋值相关

折叠代码块JAVA 复制代码
1
2
3
4
5
6
7
8
9
10
short s = 3;
// 下边两行效果一样
s = (short)s + 3;
// += 默认会强转
s += 3;

// int i = 3; i = i++;执行流程如下
temp = 3
i = i + 1;
i = temp

int i = 3; i = i++
字节码解释

流程语句

  • 分号是一句代码的结束,if后边放了分号,if就不控制后边的代码块了
    折叠代码块JAVA 复制代码
    1
    2
    3
    4
    // 这里相当于两行代码了
    if(false); {
    a = 8
    };
  • java一个大括号一个作用域。作用域外无法访问作用域内
    折叠代码块JAVA 复制代码
    1
    2
    3
    4
    5
    6
    7
    {
    a = 0;
    // 可以访问到,在一个作用域,这个变量出了作用域就被从内存删除了
    System.out.println(a);
    }
    // 访问不到,不在一个作用域
    System.out.println(a);
  • switch
    • 无论default在最前边还是最后边(default位置可以随便放),都是先判断case,最后判断default
    • 判断的时候可以使用字符串枚举进行判断,但是case不能有与判断类型不一样的判断分支.
  • break在最后挨近大括号可以不用写.因为执行完就是最后一个大括号了
  • if与switch比较
    • switch效率相对高,因为直接把所有的情况都加载到内存,if还需每个区间分别执行,效率更低
    • if更简单,数据量较少的时候用if更方便.switch写的较麻烦(switch支持枚举和字符串).
  • whilefor的区别与共通
    • 区别:while的循环变量定义在while前,while循环完之后,该变量还可以用.for循环结束,循环变量从内存销毁.
    • 共通:while(true) for(;;)(中间默认true)都可以无限循环,可以在其他条件下控制循环.
    • while和for的使用视情况而定
    • 有些情况下需要无限循环,由用户或其他条件控制退出.
  • for的其他写法
    折叠代码块JAVA 复制代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 可以在不同的地方放其他表达式
    for(System.out.println(1),System.out.println(1);System.out.println(1);System.out.println(1)){

    }

    // 也可以写多个初始化表达式,循环表达式,循环后执行的表达式
    for(int i = 0, j = 0; a<3 && b< 4; a++, b++) {

    }

常用转义符号

折叠代码块YAML 复制代码
1
2
3
4
\t(制表符): 以每个单元格进行对齐
\b(退格): 将\b后边的字符串或光标向前挪一个,覆盖原字符串
\r(按下回车键): \r后边的字符串或光标挪到本行开头,覆盖原字符串
- Windows回车是\r\n,Linux回车是\n

数组

特点

  • 同一类型数据集合
  • 是一种容器(是一个内存中的实体)
  • 建立后需要明确长度.

初始化

折叠代码块JAVA 复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 动态初始化,初始化的时候不赋值,有默认值.
String[] arr = new String[4];
// 静态初始化,初始化的时候直接赋值.
String[] arr1 = {"1". "2"};

// 二维数组初始化
// 二维数组可以只初始化第一维,第二维先不初始化
// 堆中三个第二维因为还没有初始化,所以是默认引用类型的值null
// 第一维度指向第二维的地址。
Car[][] arr2 = new Car[3][];
// 1. 第二维第一种初始化方法.上边先不赋值第二维,可以让第二维长度不同
arr2[0] = new Car[13];
// 2. 第二种初始化方法
arr2[1] = new Car[]{1, 2, 3};

二维数组使用场景

  • 数据多了用数组存,数组多了用二维数组存.
  • 存数据表

其他

折叠代码块JAVA 复制代码
1
2
3
4
// 这几种初始化也是合法形式,只不过不怎么用了
int arr[] = new int[3];
int arr[][] = new int[3][];
int[] arr[]= new int[3][];

函数

  • 函数也叫做方法,是类成员(不可以在函数中套函数)
  • publicstatic都是修饰符.public权限修饰符, static静态修饰符
  • 函数体现了封装性,是一个一个的功能
  • void默认会写return ;但是定义函数最好要有返回值.
  • 函数定义
    1. 思考结果是什么(返回值类型);
    2. 思考是否需要未知变量参与运算(参数列表).
  • 函数在内存加载过程(java Demo)
    • 自动寻找Demo类中是否有main()函数.找到会从main()执行,找不到会报错
    • 每个函数都是先进入stack执行,执行完,从stack删除.包括main()函数
    • 注意:函数执行就是在不停的进行栈操作.
    • 函数的自我调用递归
  • 方法重载(OverLoad)(可以参数个数不同,也可以参数类型不同,与返回值类型无关)
    折叠代码块JAVA 复制代码
    1
    2
    3
    4
    // void的返回类型默认有return ;.
    public int my(int a){ }
    public int my(int a,int b){ }
    public int my(double a){ }

Java虚拟机

  • Java是基于C++的优化,把C++的指针变成了Java的引用
  • JVM会在内存创建一片区域用来java程序运行
  • 会分成不同的处理区域,每个区域运算方式不同
    • 寄存器=>CPU调用
    • 本地方法区=>系统相关,linux和windows等不同
    • 方法区
    • 栈内存:存局部变量(包括变量名),是在方法中定义的东西.变量作用域结束,变量销毁.
    • 堆内存:存数组和对象(数组也是对象),new就建立在堆中
      • 堆中数据会默认初始化值.
      • 变量名赋null,相当于断了和对象的连接,堆中该对象会成为垃圾(失去了引用),被GC不定时自动回收,析构函数回收
      • C++的垃圾回收需要程序员手动执行,否则会堆溢出.(使用~)

面向对象

new: 可以看作是开辟空间的运算符.

与面向过程比较

  • 都是思想,只不过是不同的思考习惯.
  • 面向过程:一个流程,
  • 面向对象:某个东西有某功能(封装某功能),比面向过程更简洁

用面向对象写代码

  • 先想所涉及的对象都有什么,然后再去想其特性.后期就是如何维护各个对象之间的关系.
  • 一般来说名词都是对象.
  • 属性行为来描述类
  • 行为属于哪个对象,看哪个对象更清楚这个行为

注意事项

  • 一个文件只可以写一个public的类,其他的类不加public可以写到有public的文件中.
  • 类中的方法可以传递类类型.
    折叠代码块JAVA 复制代码
    1
    public void doSth(Car c){}

成员变量和局部变量

  • 成员变量定义在类中,在方法外,存在于heap中,有初值.生命周期跟随类
  • 局部变量定义在方法中,在方法内,存在于stack中,需要初始化后才能使用,生命周期跟随方法
  • 局部变量成员变量同名 ,用近的那个

成员变量和构造方法

先初始化成员变量,然后调用构造方法

匿名对象

  • 匿名对象调用
    折叠代码块JAVA 复制代码
    1
    2
    // 不给对象名称
    new Student().study();
  • 匿名对象传参
    折叠代码块JAVA 复制代码
    1
    show(new Student());

封装(Encapsulation)

作用

  • 隐藏对象的属性和实现细节.对外提供公共访问方式.

好处(可以参考电脑机箱)

  • 将变化隔离
  • 便于使用
  • 提高重用性
  • 提高安全性

封装原则

  • 将不需要对外提供的内容都隐藏起来
  • 将属性隐藏,提供公共方法来访问.(让数据更可控)

其他

  • 私有可以看作封装的一种体现.
  • gettersetter可以更好的对数据进行限制(数据范围或者数据格式等),让数据更安全
    • 变量私有化,行为公开化
    • private在内存不能访问,是因为有数字签名.
    • 将数据设置为private,使用get和set来进行获取和赋予数据(让数据更可控)
      折叠代码块JAVA 复制代码
      1
      2
      public int getAge(){}
      public void setAge(int age){}

构造函数(也可以叫构造器)

  • 函数调用会进栈,构造函数也会进入
  • 与类名相同,没有返回类型和返回值
    折叠代码块JAVA 复制代码
    1
    public 类名(){}
  • 默认调用无参构造函数.
  • 如果重写构造函数,申请对象也想不传参,就需要同时定义一个无参构造方法.
  • 构造函数可以写return ;.
  • 类是pubic,构造函数默认是public

this

  • 指向本对象,用于变量的区分.和调用该对象的方法
    折叠代码块JAVA 复制代码
    1
    2
    3
    4
    5
    // 这个构造函数进栈,相当于把自己赋给了自己.可以用this区分
    Person(String name){
    name = name;
    }
    this.speak().
  • this()的作用
    • 调用本类无参构造方法,需要放到重载构造方法第一行.(初始化需要先执行,用this()提高了代码复用性)
  • 每一个对象的函数进入栈的时候都会有一个默认的this指向所属对象.

static

  • 用来修饰成员变量和方法
  • 修饰的成员被所有对象共享
  • static修饰的可以叫做静态变量,类变量.不被static修饰的可以叫做实例变量,成员变量
  • 静态方法中不能使用非静态的成员或方法,只能使用静态成员或方法(直接使用,可能方法还没有加载到方法区)
  • 静态成员随着类加载存在,类销毁则消失.
  • 方法如果只访问静态变量可以使用静态方法,如果访问非静态变量,就需要非静态方法
  • 静态代码块可以用于类的初始化,一般是类中全是静态成员.
  • 静态方法中没有this

继承

好处

  • 提高了代码复用性
  • 类与类产生了关系,为多态提供了前提

支持单继承

  • C++支持多继承
  • Java没有多继承,是因为不同父类可能有相同的方法.可以多实现.

支持多重继承

  • A ->B -> C

继承体系

  • 查看体系中的顶层类,了解体系基本功能
  • 创建体系最子类对象,来实现更多功能

注意

  • 想要子类不完全继承父类的所有方法,父类专有或不想其他类用,就用private
  • 子类继承父类,先调用父类的构造方法(默认子类构造函数有super(),调用父类无参的).
  • 子类中构造函数默认有super()是因为继承过来的某些成员属性可能需要初始化
  • 子类不能重写父类构造函数,不能继承父类构造函数,
  • 在同一个构造函数中,super和this只能用一个,如果两个都有,本构造对象和this()都会访问父类了
    折叠代码块JAVA 复制代码
    1
    2
    3
    4
    5
    6
    7
    class Zi extents Fu{
    Zi(){
    // super();
    System.out.println();// 构造函数默认有三句,隐式的super()和return;
    // return ;
    }
    }

super

  • 代表父类空间.(没有申请对象)
  • 可以调用父类的构造函数,公有成员变量和公有方法,不能调用私有成员变量和方法.

重写(也叫覆盖,override)

  • 子类方法需要和父类方法保持一致,修饰符级别可以大于等于父类.
  • 静态只能覆盖静态函数
  • 如果参数列表不同,就不是重写了.而是另一个方法.如下就不是重写:
    折叠代码块JAVA 复制代码
    1
    2
    3
    4
    // 父类
    public int say(){}
    // 子类
    public int say(int a){}
  • 用途
    • 用新的方法,而不修改原来的方法,扩展了程序,避免了危险.
  • 弊端
    • 可能重写底层方法,导致危险.(底层不需要修改的方法用final)

final

  • 修饰过的方法在子类无法重写.无法重写只可以供每一个对象使用
    折叠代码块JAVA 复制代码
    1
    2
    3
    public static final int say(){

    }
  • 修饰过的类,子类无法继承
    折叠代码块JAVA 复制代码
    1
    2
    3
    public final class say{

    }
  • 修饰过的变量无法重新赋值.成为常量.因为不能变,所以加static,供所有对象用.特定名字,可读性更高
    折叠代码块JAVA 复制代码
    1
    2
    3
    4
    // NUMBER不能再修改值
    static final int NUMBER = 11;
    // element不能再指向其他地址
    static final Element element = new Element();

抽象

含义

  • 功能相同,但是功能的具体实现不同,可以将功能抽取出来

作用

  • 编写某一类的统一规则

样例

折叠代码块JAVA 复制代码
1
2
3
4
5
6
7
8
9
10
public abstract class Person{
public abstract void say();
public void talk(){
System.out.println(1);
}
// 抽象类作为参数,传的是子类对象
public void chat(Person person){
System.out.println(person.talk());
}
}

特点

  1. 抽象类能够实例化吗?
    • 不能实例化,抽象类只能是父类,由子类实现所有方法,实例化子类
  2. 抽象类如何用?
    • 由继承类继承,重写所有方法.
    • 继承抽象方法,扩充新的抽象方法
  3. 抽象类是否只有抽象方法?
    • 有抽象方法,也可以有非抽象方法
    • 都是非抽象方法(有方法体,没内容),却是抽象类,用于不让该类创建对象,AWT的适配器对象就是
  4. 抽象关键字可以修饰哪些东西?
    • 修饰类:被继承
    • 修饰成员方法:被重写
    • 不能修饰成员变量:修饰了没有意义
  5. 抽象类有无构造方法?
    • 有,可以子类调用
  6. 抽象关键字不可以和那些关键字共存?
    • static,加了static,该方法将属于类,但没有方法体,不能直接调用.
    • private,抽象无法重写
    • final,抽象需要重写

示例

折叠代码块JAVA 复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/*
雇员示例:
需求:
公司程序员有姓名,工号,薪水,工作内容
项目经理有姓名,工号,薪水,奖金,工作内容
根据需求建模
*/
abstract class CompanyEmployee{
private String name;
private String id;
private double pay;
CompanyEmployee(String name, String id, double pay){
this.name = name;
this.id = id;
thid.pay = pay;
}
public abstract void work();
}
class Manager extends CompanyEmployee{
private int bonus;
Manager(String name, String id, double pay, int bonus){
// 调用父类构造函数
super(name, id, pay);
this.bonus = bonus;
}
public void work(){
System.out.println("code");
}
}
class Programmer extends CompanyEmployee{
Programmer(String name, String id, double pay){
// 调用父类构造函数
super(name, id, pay);
}
public void work(){
System.out.println("manage");
}
}

接口

用途

  • 如果抽象类中的方法都是抽象的,可以用接口来表示
  • 想要功能更具有扩展性,就要多使用接口.

特点

  • 编译完之后也是class文件
  • 只能被实现
  • 接口成员都是公共的
  • 不能实例化,可以由实现了接口的子类实例化
  • 可以由类多实现:不同的接口有相同的方法,但没有方法体,所以避免了多继承的不确定性
  • 接口之间可以继承,同时可以多继承(因为没有方法体)

深层理解

  • 接口是对外暴露的规则(如何使用某功能)
  • 接口是程序的功能扩展(一个接口可以做很多事情)
  • 接口的出现降低了耦合性(两者之间的关联程度不需要太高就可以使用)(降低耦合性简称解耦)
  • 定义接口,如果参数个数过于复杂,可以不定义参数列表,交给子类用成员变量解决。

常见成员

  • 全局常量:public static final(这三个修饰符哪个不写,默认会加上没有的)
  • 抽象方法:public abstract (两个修饰符不加,也会默认加上)

接口示例

折叠代码块JAVA 复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
interface Demo{
public static final String COMPANY = "Easul";
public abstract void talk();
public abstract void say();
}
class DemoImpl implements Demo{
public void talk(){

}
public void say(){

}
}
class Test{
public static void main(String[] args){
DemoImpl d = new DemoImpl();
d.talk();
DemoImpl.COMPANY;
Demo.COMPANY;
}
}

接口代码理解

  • BookPC相当于早就写好的一个类,里边可以实现由写好的接口进行的一系列操作.因为知道由接口返回的数据是什么,所以可以提前做好处理
  • 接口是一个共性的东西,以后可能会有多种数据来源,所以不同数据来源只需要实现该接口,就相当于扩充了更多的数据来源.
    折叠代码块JAVA 复制代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    // 暴露的规则
    interface USB{
    public abstract void open();
    public abstract void close();
    }
    class BookPC{
    public static void main(String[] args){
    // 功能扩展了
    useUSB(new UPan());
    }
    // 接口类型引用
    public static void useUSB(USB u){
    u.open();
    u.close();
    }
    }

    // 实现规则
    // 实现方式和源程序关联性(耦合性降低),只需要返回相应数据即可
    class UPan implements USB{
    public void open(){
    System.out.println("open");
    }
    public void close(){
    System.out.println("close");
    }
    }

接口与抽象类的异同

  • 相同
    • 都是向上抽取共同的成员
  • 不同
    • 抽象类定义某体系基本内容.
    • 接口定义体系的额外功能.

多态

理解

  • 相当于猫科动物有老虎,狮子,猫等
  • 可以想成是多种形态.例如函数的重载可以看成多态,子父类中的同名参数可以看成多态.
  • 对象的多态就是既有本类的形态,又有父类的形态.
  • 对象多了,操作起来麻烦,所以可以用父类来统一操作.

好处

  • 提高了代码扩展性,前期代码可以使用后期内容

坏处

  • 无法使用子类特有方法.

向上造型

折叠代码块JAVA 复制代码
1
2
// 父亲在上,向上造型(自动类型提升)
Father f = new Son();

向下造型

折叠代码块JAVA 复制代码
1
2
// 儿子在下,向下造型(为了使用子类特有方法)
Son s = (Son)f;

常见错误

折叠代码块JAVA 复制代码
1
2
3
4
5
6
7
// a没有指向Cat,因此和Cat没关系,所以不能强转
Animal a = new Animal();
Cat c = (Cat) a;

// a指向了Dog,没有指向Cat,所以不能强转
Animal a = new Dog();
Cat c = (Cat) a;

成员变量

折叠代码块JAVA 复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Fu{
int num = 3;
}
class Zi extends Fu{
int num = 4;
}
// 因为向上转型,f指向了父类空间,所以只读取父类的成员变量,不读取子类成员变量.
// 编译时,查看该引用的类中是否有该变量,运行时,同样查看该引用的类中是否有该变量.
class Test{
public static void main(String[] args){
Fu f = new Zi();
f.num;
}
}

成员函数

折叠代码块JAVA 复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Fu{
void show(){
System.out.println(1);
}
}
class Zi extends Fu{
void show(){
System.out.println(12);
}
}
class Test{
// 编译时,查看该引用的类中是否有该方法
// 运行时,f指向的对象是子类地址,所以运行子类的方法
// 找不到子类中的show(),再去父类找
// 子类方法已经更新,所以先用子类方法
public static void main(String[] args){
Fu f = new Zi();
f.show();
}
}

静态函数

折叠代码块JAVA 复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Fu{
static void show(){
System.out.println(1);
}
}
class Zi extends Fu{
static void show(){
System.out.println(12);
}
}
class Test{
public static void main(String[] args){
// 显示父类的静态方法
// 编译时,查看该引用的类中是否有该静态方法
// 运行时,同样查看该引用的类中是否有该静态方法.只看本类
// 静态不需要对象.类名调用即可,所以和子类覆盖的方法没关系
Fu f = new Zi();
f.show();
}
}

自始自终都是子类对象在进行转换

折叠代码块JAVA 复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
abstract class Animal{
public abstract void eat();
}
class Dog extends Animal{
public void eat(){

}
}
class Pig extends Animal{
public void eat(){

}
}
class Test{
public static void main(String[] args){
method(new Dog());
method(new Pig());
}
// 可以传入所有Animal的子类,就不用重新写方法了,不然每一种对象都需要写一个方法
public static void method(Animal animal){
animal.eat();
}
}

native

修饰本地方法,最后会调用底层C的方法

内部类

特点

  • 内部类可以直接访问外部类所有成员
  • 外部类访问内部类,需要创建内部类的对象
  • 相当于一种封装
  • 可以被修饰符修饰
  • 内部类方法重写的时候权限不能小于外部类
  • 静态只能覆盖静态函数

用途

分析事物的时候,事物内部还有事物,且内部事务需要访问外部事物的成员.

折叠代码块JAVA 复制代码
1
System.out.println()

成员内部类

相当于类的一个普通成员

折叠代码块JAVA 复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class Test {
public static void main(String[] args) {
// 第一种创建方式,不多见,private就不能用了
MySame.InnerClass e = new MySame().new InnerClass();
e.say();
// 第二种创建方式,可以用于private
MySame.InnerClass e1 = new MySame().createInner();
e1.say();
}
}

public class MySame {
private int number = 1;
public InnerClass inner = null;

// 外部类返回内部类对象
public InnerClass createInner() {
if (inner == null)
inner = new InnerClass();
return inner;
}

// 外部类访问内部类方法
public void method() {
if (inner == null)
inner = new InnerClass();
inner.say();
}

// 成员内部类
public class InnerClass {
public void say() {
// 可以访问外部类所有成员
System.out.println(number);
}
}
}

静态内部类

特点

  • 相当于类的一个静态成员
  • 外部类一加载,内部类会同时加载,可以直接调用.相当于一个外部类
  • 内部类是静态的,外部类成员是静态的,那么可以用内部类的静态方法直接访问外部类成员,不用申请对象.
  • 内部类方法是静态,内部类也需要是静态,不然内部类方法无法加载.

示例一

折叠代码块JAVA 复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class Test {
public static void main(String[] args) {
// 第一种创建方式,内部类是静态的,可以直接外部类用类名访问,然后申请内部类的对象,用于访问内部类中非静态成员
// 参考System.out.println()
MySame.InnerClass e = new MySame.InnerClass();
e.talk();
// 第二种创建方式,通过单例模式返回对象
MySame.InnerClass e1 = new MySame().createInner();
e1.talk();
// 第三种创建方式,访问内部类静态方法,可以直接调用,不需要申请对象
// 类似System.out.println()
MySame.InnerClass.say();
}
}

public class MySame {
private static int num = 3;
public InnerClass inner = null;

public InnerClass createInner() {
if (inner == null)
inner = new InnerClass();
return inner;
}

// 静态内部类
public static class InnerClass {
public void talk() {
System.out.println(2);
}

public static void say() {
// 静态内部类只能访问外部类静态的num,并且自己也必须是静态方法
System.out.println(num);
}
}
}

示例二

折叠代码块JAVA 复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 内部类可以访问外部类的成员是因为持有了外部类引用
class Outer{
int num = 3;
class Inner{
int num = 4;
void show(){
int num = 5;
// 当前方法进栈,输出方法内的num为5
System.out.println(num);
// this指向本对象,输出成员变量的num为4
System.out.println(this.num);
// 部类成员变量,输出方法内的num为3
System.out.println(Outer.this.num);
}
}
void method(){
// 匿名对象
new Inner().show();
}
}
class Test{
public static void main(String[] args){
new Outer().method();
}
}

局部内部类

在方法中写一个类(个人感觉是想要在方法中嵌套其他方法采用的,java不能在方法中套方法)
方法中的内部类访问局部变量,局部变量需要声明为final类型.因为方法进栈,局部变量进栈,方法出栈,局部变量出栈,show()方法中用的x可能就没有了,所以用final,把x变成常量,然后show()方法中的数就是固定的9.

折叠代码块JAVA 复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Outer{
// 传进来的变量也可以用final修饰
Object method(final int y){
// final修饰了x之后,show()里的x加载的时候就已经成为常量9,不再变
final int x = 9;
class Inner{
void show(){
System.out.println(x);
System.out.println(y);
}
}
Object in = new Inner();
return in;
}
}
class Test{
public static void main(String[] args){
object obj = new Outer().method(19);
}
}

匿名内部类

特点

  • 必须继承或实现一个外部类或接口.(new Chat()就是在实现一个接口)
  • 可以理解为创建了子类对象.

使用场景

  • 函数的参数是接口类型,且接口中的方法不超过3个.用匿名内部类作为参数传递.

示例

折叠代码块JAVA 复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
interface Chat{
public abstract void English();
}
// 第一种,需要赋值给变量,用于多次使用.创建接口的对象,然后重写所有的方法
// 相当于多态,右边创建了子类对象
// 可以理解为class ChatImpl implements Chat{},然后创建ChatImpl对象
Chat c = new Chat(){
public void English() {
System.out.println();
}
};
c.English();

// 第二种,只使用其中一个方法
new Chat(){
public void English() {
System.out.println();
}
}.English();

// 第三种,在某个方法中传入指定对象
public class MySame{
// 需要传入实现了Chat的对象
public void show(Chat c){

}
}
public class Test {
public static void main(String[] args) {
MySame e = new MySame();
e.show(new Chat(){
// 直接实现接口和抽象方法来传入一个对象
public void English() {
System.out.println();
}
});
}
}

Object类

含义

  • 所有类超类
  • 具有所有对象都具有的共性内容

常用方法

折叠代码块YAML 复制代码
1
2
3
4
5
6
equals(Object): 默认判断两个引用是否指向同一对象.重写时一般需要重写hashCode方法
- p1 == p2两个对象比较的是地址值
hashCode(): 默认得到对象的十进制存储地址.可以重写该方法赋予每一个对象自定义唯一标识
- 同一个对象需要有同样的hashCode()
Class<> getClass(): 得到运行对象的字节码文件对象
toString(): 默认获取类名(getClass().getName()获取)@十六进制hashCode值(hashCode()获取)
折叠代码块JAVA 复制代码
1
2
3
Student s = new Student();
// 输出自动调用toString()方法
System.out.println(s);

equals()重写示例

折叠代码块JAVA 复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class Test{
public static void main(String[] args){
System.out.println(new Person(20).equals(new Person(20)));
}
}
class Person{
private int age;
Person(int age){
this.age = age;
}
// 重写需要和父类函数列表一样
public boolean equals(Object obj){
// 传入非合法类型,就可以抛异常,不抛异常可能会影响接下来的运行错误
if(!(obj instanceof Person)){
// 可以自定义异常类,抛出之后可以更精确描述错误
throw new ClassCastException("类型错误");
}
// 不能用obj来接收转型,因为obj是Object类型
Person p = (Person)obj;
return this.age == p.age;
}
}
// 更加精细的重写
public boolean equals(Object o) {
// 是本对象,返回true
if (this == o)
return true;
// 传入对象是空或者两个对象不是同一个类,返回false
// 也可以用 o instanceof Student来判断是否是需要的类
if (o == null || getClass() != o.getClass())
return false;
// 将传入对象强转为需要类型,否则没有相关变量于方法
Element element = (Element) o;
if(FIRST_NUMBER != element.FIRST_NUMBER)
return false;
if(getName() == null){
if(element.getName()!=null){
return false;
}
// 调用字符串是否相等的方法
} else if(!getName.equals(element.getName)){
return false;
}
return true;
}

getClass()

用于获取的字节码文件对象
每一个字节码文件加载到方法区之后,会自动为其在堆创建一个Class类的对象(字节码文件对象),下边为它的构造

折叠代码块JAVA 复制代码
1
2
3
4
5
6
7
8
9
10
class Class{
// 方法名
name
// 字段
field
// 构造器
constructor
// 方法
method
}

后期创建对象,就根据这个字节码文件对象创建新的对象

折叠代码块JAVA 复制代码
1
2
3
4
5
6
7
8
9
10
11
12
class Test{
public static void main(String[] args){
Person p1 = new Person(1, "E");
Person p2 = new Person(2, "W");
Class<? extends Person> c1 = p1.getClass();
Class<? extends Person> c2 = p2.getClass();
// 相等
System.out.println(c1 == c2);
// 获取这个字节码文件对象的类名
System.out.println(c1.getName());
}
}

image

作用

  • 就是个文件夹,在文件第一行
  • 给类文件分类管理
  • 可以给类提供多层命名空间
  • 也是一种封装形式

定义

  • package mypackage,包名称小写。定义自己的包初始位置。

运行

  • mypackage.Demo,运行需要加入包名,且类需要放到mypackage文件夹下
  • 导入别人的类库,需要放到顶层目录,否则会编译出错。

导包(导入的是包中的类,不导入包中的包)

  • 不用写包名,可以直接使用类。
  • 导入的位置是从当前顶层包所在的位置算起。
  • import test.*;导入test文件夹中所有类,不包括子包的类。

示例

折叠代码块 复制代码
1
2
3
4
5
6
7
8
9
test
├── bin
└── test
├── mypackage
│ └── Demo.java
├── demoa
│ └── DemoA.java
└── demob
└── DemoB.java
折叠代码块JAVA 复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Demo.java
package test.mypackage;
import test.demoa.DemoA;
import test.demob.DemoB;
class Demo{
public static void main(String[] args){
DemoA d = new DemoA();
d.show();
System.out.println(1);
}
}
// =============================================
// DemoA.java
package test.demoa;
import test.demob.DemoB;

public class DemoA extends DemoB{
public void show(){
System.out.println("asdf");
method();
}
}
// =============================================
// DemoB.java
package test.demob;

public class DemoB{
protected void method(){
System.out.println("asdfgh");
}
}

进入到test文件夹,然后执行下边的命令

折叠代码块BASH 复制代码
1
2
3
4
5
6
# 将test目录及子目录中的java文件都编译bin目录,自动生成包目录
# -d 编译到哪个目录
javac -d ./bin/ ./test/**/*.java
cd bin
# 运行main方法的类
java test.mypackage.Demo

Jar

含义

  • java的压缩包,可以把多个包合并成一个包
  • 是一个命令行工具

用法

折叠代码块BASH 复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# -c:创建归档文件
# -f:设置归档文件名称
# -v:列出详细信息
# -t:列出jar包中的内容
# -x:解压jar包
# 将test目录归档到mytest.jar中
jar -cvf mytest.jar test
# 查看mytest.jar中的文件信息
jar -tvf mytest.jar test
# 解压jar包
jar -xvf mytest.jar test
# 添加jar到CLASSPATH
# windows:set classpath=./mytest.jar;%classpath%
# linux
export CLASSPATH=./mytest.jar:$CLASSPATH
# 运行main函数的class
java test.mypackage.Demo

异常

含义

  • 运行时期发生的不正常情况
  • 放到非法情况下抛出,可读性和可维护性更强.
  • 将问题封装成对象,通过抛出,告诉调用者.(分开了正常流程代码和异常处理代码)
  • 不同的问题用不同的类描述
  • 可能出现问题,就抛出异常。

抛出信息组成

  • 异常所在的线程名称,异常名称,异常信息,异常位置。

体系

  • Throwable(它和其子类才具有可抛性)
    • 可处理的(Exception类)
      折叠代码块YAML 复制代码
      1
      2
      编译时不检测异常: RuntimeException(正常运行时异常)及其子类,问题的发生更多由调用者或内部状态改变导致
      编译时被检测异常: Exception和子类都是(RuntimeException体系除外),需要在编译时就处理该问题
    • 不可处理的(Error类,由JVM抛出的严重问题)
      折叠代码块YAML 复制代码
      1
      2
      NoClassDefFoundError: (未找到调用类错误)
      OutOfMemoryError: (内存溢出错误)
  • 通过throws(在函数上声明可能的异常类)和throw(抛出异常对象)来体现抛出
  • 子类后缀名都用父类名做后缀,提高了阅读性

理解

  • 异常类如果继承了Exception类,发生了异常,可以处理,catch;处理不了,throws抛出,编译时检查
  • 异常类如果继承了RuntimeException类,发生了异常,不需要throws,也可以throws或catch,编译时不检查

异常的执行流程

折叠代码块JAVA 复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// main()进栈,创建int数组和Demo对象,然后调用method()方法
// 执行method()中的语句的时候发现角标不存在,并且认识该异常
// 然后创建了throw new ArrayIndexOutOfBoundsException()(问题封装)
// 抛给了main()函数(上级调用者)。
// main()抛给JVM(上级调用者),然后JVM调用异常处理机制进行处理(输出异常相关信息)
class Test{
public static void main(String[] args){
int[] arr = new int[3];
Demo d = new Demo();
d.method(arr, 3);
}
}
class Demo{
public void method(int[] arr, int index){
// 发现异常,throw new ArrayIndexOutOfBoundsException()抛出异常
System.out.println(arr[index]);
}
}

定义抛出异常

折叠代码块JAVA 复制代码
1
2
3
4
5
6
7
8
9
10
11
12
class Demo{
public int method(int[] arr, int index){
if(arr == null)
throw new NullPointerException("数组引用不能为空");
}
if(index >= arr.length){
// 可以加自己的信息
throw new ArrayIndexOutOfBoundsException("数组的角标越界:" + index);
}
return -1;
}
}

自定义异常类

折叠代码块JAVA 复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
// 需要进行声明或者抛出
// 第一种:继承了非RuntimeException类
// 需要每个类使用的方法都进行声明(出现问题自动抛出),否则编译失败
class MinusIndexException extends Exception{
MinusIndexException(){
}
MinusIndexException(String msg){
// 调用父类的构造函数抛出需要抛出的信息
super(msg);
}
}
class Demo{
// 需要在声明处标识有什么问题,内部封装看不到代码,只能看声明,方便调用者查看及处理
// 每一个调用类都需要声明
public int method(int[] arr, int index) throws MinusIndexException{
if(index < 0){
throw new MinusIndexException();
}
return array[index];
}
}
class Test{
public static void main(String[] args) throws MinusIndexException{
int[] arr = new int[3];
Demo d = new Demo();
d.method(arr, -3);
}
}

// 第二种:继承了RuntimeException,异常类编译时不检测,不需要声明
class MinusIndexException extends RuntimeException{
MinusIndexException(){
}
MinusIndexException(String msg){
super(msg);
}
}
class Demo{
public int method(int[] arr, int index){
if(index < 0){
throw new MinusIndexException();
}
return array[index];
}
}
class Test{
public static void main(String[] args){
int[] arr = new int[3];
Demo d = new Demo();
d.method(arr, -3);
}
}

// 第三种:捕捉出现的异常,然后进行处理,处理完可以执行接下来的代码
class MinusIndexException extends Exception{
MinusIndexException(){
}
}
class Demo{
//
public int method(int[] arr, int index) throws MinusIndexException, NullPointerException{
if(index == null){
throw new NullPointerException();
}
if(index < 0){
throw new MinusIndexException();
}
return arr[index];
}
}
class Test{
public static void main(String[] args){
try{
int[] arr = new int[3];
Demo d = new Demo();
// 调用的时候出现异常,异常会返回到这里,然后再交给相应catch
d.method(arr, -3);
// 相当于MinusIndexException e=new MinusIndexException()
} catch(MinusIndexException e){
// 获取抛出的异常信息
System.out.println(e.getMessage());
// 获取异常类名和抛出的异常信息
System.out.println(e);/
// 直接打印堆栈信息,jvm的默认处理机制就调用了这个方法
e.printStackTrace();
System.out.println("负数角标异常");
} catch(NullPointerException e){
// 输出e会默认调用toString()
System.out.println(e.toString());
// 多catch,放到上边会出错
} catch(Exception e){
// 如果有其他异常可以这么做,也可以不管,查看并处理新的异常
System.out.println(e.getMessage());
// return ;如果这里用了这个返回,整个函数就会结束,但是finally还是会运行
// System.exit(0);退出JVM,这样的话finally也不会执行
} finally{
System.out.println("finally");
}
// try catch finally执行完,会继续执行这里的代码
System.out.println(11);
}
}

finally的作用

  • 用于释放资源
  • 用于关闭数据库

try catch finally的组合

  • try catch finally
  • try catch:用于没有资源需要释放的情况
  • try finally:(常用)用于异常无法catch,但需要关闭资源(需要throws)

Java编译器检查过程

  • 检查是前边检查完才检查后边
  • 检查语法错误=>>检查编译错误(异常等)

异常处理

  • 真实开发异常输出到log日志
  • 异常执行,会阻断剩下的所有代码执行

异常的注意事项

  • 子类在覆盖父类方法时,父类的方法如果抛出了异常,子类的方法只能抛出父类的异常或该异常的子类(也可以不抛)
    折叠代码块JAVA 复制代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class AException extends Exception{
    }
    class BException extends AException{
    }
    class CException extends Exception{
    }
    class Fu{
    void show() throws AException{
    }
    }
    class Zi extends Fu{
    // 可以抛出AException或BException
    void show() throws BException{
    }
    }
  • 如果子类抛出的是父类没有的异常,那么会出问题
    折叠代码块JAVA 复制代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    class AException extends Exception{
    }
    class BException extends AException{
    }
    class CException extends Exception{
    }
    class Fu{
    void show() throws AException{
    }
    }
    class Zi extends Fu{
    // 子类抛出父类没有抛出的异常
    void show() throws CException{
    }
    }
    class Test{
    public static void main(String[] args){
    // 正常抛出
    methos(new Fu());
    // 利用多态,运行的时候子类的异常父类没有,无法抛出
    methos(new Zi());
    }
    public static void method(Fu f){
    f.show();
    }
    }
  • 父类抛出多个异常,子类只能抛出父类子集。
  • 如果父类的方法没有抛出异常,子类也不能抛出异常,只能try
    折叠代码块JAVA 复制代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    interface Inter{
    void function();
    }
    class D implements Inter{
    // 父类没有抛出异常,子类不能抛出异常
    void function(){

    }
    }

异常+面向对象的分析

  • 异常转换(异常封装):接收到一个异常,但是抛出了上级更能明白和解决的异常
  • 关于“毕老师用电脑上课”进行分析,有两个对象,一个是老师,一个是电脑,老师使用电脑
  • 电脑可以运行(run()),可以重启(reset()),老师有名字(name),有电脑(computer),可以讲课(prelect())(方法和谁关联性更大就给谁)
  • 老师讲课的时候(prelect()),需要让电脑打开,然后讲课。
  • 在讲课过程中电脑可能出现问题:蓝屏,冒烟(用异常类封装状态),因为是电脑的问题,所以给电脑定义state记录电脑状态值,然后运行时(run())根据状态值判断电脑运行时是否出现问题。出现问题就创建异常对象并抛出。
  • 老师讲课的时候(prelect())可能电脑会出现问题,相关代码使用try运行, 出现问题,用catch来接。
    • 蓝屏,重启电脑即可,调用电脑的重启方法(reset()),然后继续讲课
    • 电脑冒烟,自己无法处理,老师布置自我练习的任务(test()),然后将问题上抛。使用异常转换,告诉上级可以看得懂的信息
  • 公司需要毕老师进行讲课,创建毕老师这个对象,然后调用讲课方法(perlect()),但是讲课可能出问题,所以需要接收并处理异常信息,例如让另一个老师讲
    折叠代码块JAVA 复制代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    class Computer{
    private int state = 2;
    public void run() throws LanPingException,MaoYanException{
    if(state == 1)
    throw new LanPingException("电脑蓝屏");
    else if(state == 2)
    throw new MaoYanException("电脑冒烟");
    System.out.println("电脑运行");
    }
    public void reset(){
    state = 0;
    }
    }
    class Teacher{
    private String name;
    private Computer computer;
    Teacher(String name){
    this.name = name;
    computer = new Computer();
    }
    public void prelect() throws NoPlanException{
    try{
    computer.run();
    System.out.println("讲课");
    }
    catch(MaoYanException e){
    System.out.println(e);
    // 学生练习
    test();
    // 异常转换(异常封装):接收到一个异常,但是抛出了上级更能明白和解决的异常
    // 异常不抛出,相当于隐藏了异常,会有问题。
    // 无法解决的问题,继续抛出
    throw new NoPlanException("课时进度无法完成"+e.getMessage());
    } catch(LanPingException e){
    System.out.println(e);
    computer.reset();
    System.out.println("电脑重启");
    prelect();
    }
    }
    public void test(){
    System.out.println("学生练习");
    }
    }
    class Company{
    public static void main(String[] args){
    Teacher t = new Teacher("毕老师");
    try{
    t.prelect();
    }
    catch(NoPlanException e){
    System.out.println(e);
    System.out.println("换人");
    // 创建一个新的老师
    Teacher t = new Teacher("Easul");
    }
    }
    }
    class LanPingException extends Exception{
    LanPingException(String msg){
    super(msg);
    }
    }
    class MaoYanException extends Exception{
    MaoYanException(String msg){
    super(msg);
    }
    }
    class NoPlanException extends Exception{
    NoPlanException(String msg){
    super(msg);
    }
    }

  • 类的销毁随着jvm的销毁而产生.
  • 如果加载的类过多,GC可以对类进行回收(类也是一种对象),长时间不用的就回收.
  • 类的一般初始化过程:默认初始化(成员变量加载到内存,赋值0等默认值)==>显式初始化(成员变量赋值后边等号给的值)==>构造初始化(构造函数初始化)

权限修饰符

image

instanceof

用途

  • 判断对象的具体类型,只能判断引用类型(一般用于强转前,防止强转失败,增强代码健壮性)

示例

折叠代码块JAVA 复制代码
1
2
3
4
5
6
7
8
9
public void method(Animal a){
if(a instanceof Cat){
Cat c = (Cat) a;
c.catchMouse();
} else if(a instanceof Dog){
Dog d = (Dog) a;
d.woof();
}
}

工具类

  • 一个非静态方法都没有,通常是工具类,只有静态方法.
  • 不需要创建对象,可以将构造函数私有化.创建对象反而消耗内存.

设计模式

对问题行之有效的解决方式

单例设计模式

  • 保证一个类在内存中只有一个对象,防止不停创建对象消耗资源
  • 可用于配置信息的修改(公有动态信息)

实现方式:

  • 私有化构造函数
  • 创建好本类实例
  • 提供一个方法获取本类实例

相关代码

饿汉式用的多,懒汉式在多线程可能保证不了对象唯一性,但懒汉面试用的多

折叠代码块JAVA 复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/*
饿汉式
类一加载,对象就存在
*/
public class Tool{
private static Tool tool = new Tool();
private Tool(){

}
/*用方法返回,可以让数据可控*/
public static Tool getInstance(){
return tool;
}
}
/*
懒汉式
类加载.没有对象,调用getInstance()创建
延迟加载形式
*/
public class Tool{
private static Tool tool = null;
private Tool(){

}
// 用方法返回,可以让数据可控
public static Tool getInstance(){
if(tool == null)
tool = new Tool();
return tool;
}
}

单例解释

  • main()函数和其所属类进入方法区,然后main()进栈
  • 使用Single类,构造方法进非静态方法区,变量s和getInstance()进入静态方法区.s默认值为null
  • 在堆中创建Single对象,然后将地址赋给s
  • 在main()中,访问getInstance()方法,它进栈,
  • 返回对象的地址,之后弹栈,地址赋给s1,s2也进行相同的操作
    image

主函数的解释

JVM只调用和固定格式一样的主入口

折叠代码块JAVA 复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// public: 主函数需要最大的权限
// static: 调用的时候不需要对象
// void: 主函数不需要返回值
// main: 固定函数名,jvm才可以识别
// String[] args: 主函数参数列表,运行的时候可以带到后边
// 因为main函数有参数列表,所以调用的时候必须要传参才能调用,而这里会默认传new String[0];(传的长度为0的数组).
class test{
public static void main(String[] args){

}
// 加上main函数的重载,JVM也只调用上边的String的入口,因为调用是固定
public static void main(int x){

}
}

堆, 栈, 方法区的存储

  • 栈:普通变量, 调用的方法
  • 堆:对象,对象中的成员变量
  • 方法区:类, 方法, 静态成员
    • 静态区:静态变量, 静态方法,
    • 非静态区:成员方法,构造方法

代码块

局部代码块

可以定义局部变量的生命周期,这个变量不用的时候就可以自动从内存中删除掉

折叠代码块JAVA 复制代码
1
2
3
4
5
6
7
public void say(){
{
// 生命周期只在方法中的代码块中
int number = 1;
}
System.out.println(number);
}

构造代码块

用于给所有对象初始化相同的数据,构造函数是给不同对象初始化特有数据

折叠代码块JAVA 复制代码
1
2
3
4
5
private double age = 1;
{
// 创建对象后,创建对象成员时就运行.然后再调用构造方法
System.out.println(age);
}

静态代码块

折叠代码块JAVA 复制代码
1
2
3
4
static{
//静态代码块只在类加载时执行一次,放到方法区后就不执行了
System.out.println(1);
}

各种成员加载顺序

Created with Raphaël 2.2.0静态成员静态代码块子类和父类非静态成员变量加载初始化父类成员变量(和构造代码块)父类构造函数初始化子类成员变量(和构造代码块)子类构造方法调用普通方法(同时调用局部代码块)

Java类编译

  • Java编译一个带有main()入口的class,如果发现在这个class中需要的某个类没有,那么就会在classpath进行查找,查找不到就会继续找同名的java文件,然后编译该文件.
  • 也可以直接使用javac *.java(同时编译某目录所有文件)

三种引用类型

数组类型,类类型,接口

内存分配相关

数据初始化的内存分配

  • stack(栈),分配快,小
  • heap(堆),分配慢,大
  • 对于int[] arr = new int[3],arr存到栈,也就是存一个变量,实际数组存到堆,
  • new的数据在堆里生成之后,会有一个地址,栈里的变量指向这个地址.数据生成后根据数据类型赋了默认值.
  • 栈里的数据只是起指向,没有默认值.
  • int x = 0,变量和0都存到栈里,因为没有new.使用包装类就存到堆里了
    image

二维数组在内存的分配

int[][] arr = new int[3][2];

  • 先创建第一维,第一维排列在一块,默认指针为null
  • 然后创建每一个二维,创建好了之后,将每一个二维的首地址赋给一维的那个地址.
  • 因此可以对应二维数组第二维可以先不创建的情况
    image
    image
    折叠代码块JAVA 复制代码
    1
    2
    3
    4
    5
    6
    7
    int[][] arr = new int[3][]
    // 输出两个[[的地址
    System.out.println(arr);
    // 输出null,因为一维还没有创建
    System.out.println(arr[0]);
    // 空指针异常,因为第二维没有指向
    System.out.println(arr[0][0]);

构造函数在内存中的运行

  • 先在stack加载main(),然后创建p2,相应的对象创建到堆里
  • 给对象创建相应的成员变量,并赋予初始值
  • 构造方法进栈,给n和a赋值,没有相应的变量,于是调用默认的this(),找到自己所属对象,为变量赋值.
  • 赋值后弹栈,将对象地址赋值给p2
    image

对象的初始化在内存的分配

  • 运行StaticDemo2这个类,那么先将类加载到方法区(方法区左边是非静态区,右边是静态区)
  • 先将静态方法main()加载到静态方法区,然后将构造方法StaticDemo2(){}加载到非静态方法区.
  • main()方法入栈,开始一句一句执行.
  • 执行第一句,加载Person类到方法区,也就是加载Person类中的构造方法Person(String name, int age),show()方法到Person类的非静态方法区,country, method()到静态方法区.
  • 调用method()方法,将方法从方法区入栈,从方法区找到并输出country,然后method()弹栈
  • 执行第二句,变量p入栈,堆中创建Person的对象,并创建成员变量age, name,然后赋初值
  • Person的构造函数入栈,传入参数,然后由this指向,将参数赋值给堆中的对象中的成员变量.
  • 赋值完之后构造函数弹栈,Person对象的地址赋给p
  • 执行第三句,调用show()方法,输出country, name, age,则从栈->堆->方法区的顺序依次找到变量输出
  • 调用完弹栈.
  • main函数执行完弹栈.
    image

静态成员的使用在内存的分配

静态成员特点

  • 静态方法不能使用非静态方法是因为非静态的在堆中,静态的在方法区中,没有申请对象就无法调用该方法.
  • 静态成员先于对象产生
  • 静态成员属于类,
  • 静态成员可以让对象共用,新的修改会覆盖掉旧的数据.

内存分配解析

折叠代码块JAVA 复制代码
1
2
3
4
public class Animal{
public static int age;//可以不赋初值,只有final需要赋初值
}
System.out.println(Animal.age);
  • 当运行Animal.age时,先加载Animal.class到方法区
  • 在方法区开辟一块属于Animal.class的空间(静态区域),存放类中静态的变量或方法
    image

super在内存的分配

  • ExtendsDemo2.class,main()函数加载到方法区,然后main()进栈,执行第一句
  • 执行的时候,将Fu类和相应的构造方法加载到方法区,然后加载Zi类和相应的构造方法,普通方法.Zi类的super指向父类空间
  • 在栈中创建变量z,在堆中创建对象Zi,然后分两块区域,一块是Fu的,一块是Zi的
  • 将Fu的成员变量加载到Fu在堆的区域,然后将Zi的成员变量加载到Zi在堆的区域.(Fu类没有申请对象,所以在堆和方法区只放了父类空间)
  • 对象创建完成,将地址赋给z
  • 执行第二句代码,show()方法进栈,this找到Zi的num和super找到Fu的num输出,然后函数弹栈
  • 最后main()弹栈
    image

子类调用父类更详细的内存解释

折叠代码块JAVA 复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 参考代码
class Father{
int number = 4;
{
System.out.println("first"+number);
}
Father(){
show();
return ;
}
void show(){
System.out.println("F>>");
}
}
class Son extends Father{
int number = 4;
{
System.out.println("second"+number);
}
Son(){
super();
System.out.println("middle");
return ;
}
void show(){
System.out.println("s>>"+number);
}
}
class test{
public static void main(String[] args){
Son s = new Son();
s.show();
}
}
  • ExtendsDemo5.class和main()方法加载到方法区,main()进栈并执行
  • 创建Zi类对象,先将Fu类和其构造函数,普通函数加载到方法区,然后再加载子类的构造函数和普通函数加载到方法区.
  • 在栈中创建变量z,在堆中创建对象,并将Zi的成员变量加载到堆(这个时候只加载了变量,并没有初始化)
  • 调用Zi类的构造函数中的super(),先初始化父类.
  • Fu()类进栈,调用show()方法,此时Fu()指向this当前对象,所以会先找Zi在方法区的该方法,然后在Zi的堆空间中找num,输出相应结果.这个时候因为子类成员变量只加载,还没有初始化,所以num为0
  • 执行完super()后,开始先将Zi成员变量初始化,然后继续执行Zi构造函数中接下来的语句.这个时候num成为8
  • Zi构造函数执行完成,对象创建完成,将地址赋值给z,然后执行z.show()
    image

重载内存图

调用show()方法的时候,会默认先从子类找,如果子类没有,通过super指向父类空间,再从父类空间找.
image

一些小算法

  • 累加算法:对一个元素进行规律性操作并记录下来.
  • 计数器思想:对满足的某一条件进行计数
  • 大圈套小圈:一种重复情况中的每一次都对应多种情况.

排序算法

冒泡排序

每一个和下一个比,留下了最后一个(bubbleSort)

折叠代码块JAVA 复制代码
1
2
3
4
5
6
7
8
9
for(int i = 0; i < 5 - 1; i++){
for(int j = 0; j < 5 -1-i; j++){
if(arr[j] > arr[j+1]){
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}

选择排序

第一个不动,依次和后边的比较(selectSort)

折叠代码块JAVA 复制代码
1
2
3
4
5
6
7
8
9
for(int i = 0; i < 5 - 1; i++){
for(int j = i+1; j < 5; j++){
if(arr[i] > arr[j]){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}

算法优化:这里每次都交换,可以保存下标,只交换最后一次(最后找到最小的数,然后交换即可,)

折叠代码块JAVA 复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
for(int i = 0; i < 5 - 1; i++){
int index = i;
int temp;
for(int j = i+1; j < 5; j++){
if(arr[index] > arr[j]){
index = j;
}
}
if(index != i){
temp = arr[index];
arr[index] = arr[i];
arr[i] = temp;
}
}

注意:Java中给函数传递数组时传递的是引用,那么就可以直接在函数中交换数组数据即可完成数组元素的交换.不需要返回数组.(堆中的数据已经进行了交换)

插入排序

希尔排序

快速排序

归并排序

堆排序

查找算法

顺序查找(用于无序)

折叠代码块JAVA 复制代码
1
2
3
4
5
6
7
8
// 查找数组某元素,返回索引值
public static int getIndex(int[] arr, int number){
for(int i = 0; i < arr.length; i++){
if(arr[i] == number)
return i;
}
return -1;
}

折半查找(用于有序):

特点:大索引小于小索引就说明判断结束了

折叠代码块JAVA 复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 折半查找(二分查找)
public static int halfIndex(int[] arr, int number){
int max = arr.length - 1;
int min = 0;
int mid = (max + min) / 2;
while(arr[mid] != number){
if(arr[mid] > number)
max = mid - 1;
else
min = mid + 1;
if(max < min)
return -1;
}
return mid;
}
// 折半查找(二分查找),用min<=max作为界限
public static int halfIndex(int[] arr, int number){
int max = arr.length - 1;
int min = 0;
while(max < min){
int mid = (max + min) >> 1;
if(arr[mid] > number)
max = mid - 1;
else if(arr[mid] < number)
min = mid + 1;
else
return mid;
}
return -1;
}

分块查找

 评论
来发评论吧~
Powered By Valine
v1.5.2