CGBTN2111汇总复习

CGBTN2111汇总复习

复习思路

先跟着老师走,遇到会的,快速过,遇到不会的,先做记录,后面再复习.

一阶段学习路径

在这里插入图片描述

1 基础语法Basic

1.基础环境配置

1. 安装JDK

JDK:Java开发工具包,我们使用的版本是1.8
注意:一台PC上可以安装多个JDK,具体环境变量配置哪个JDK,哪个就生效

2. 环境变量的配置

JAVA_HOME : 配置的是JDK安装的目录
Path : 配置的是JDK的bin目录,不新建的
CLASS_PATH:配置的是JDK的lib目录
win+R键,在运行窗口输入cmd
验证命令为 : java -version 出现JDK版本号即为成功

3. 开发工具

eclipse IDEA
注意1:开发工具无需纠结,重要的是编程的思路,对于工具而言,选一个自己喜欢的就好,重要的是提高这个自己常用软件的熟练度(快捷键 字体设置 配置JDK…面向百度进行开发)
注意2:大家在安装的时候,不要选择C盘系统盘,而且路径中不要出现中文或者空格等等其他特殊符号,因为会出现一些未知的问题

2.JDK JRE JVM

JDK:Java开发工具包(Java Development Kit)–开发的最小单位
JRE:Java运行时环境(Java Runtime Environment)–运行的最小单位
JVM:Java虚拟机(Java Virtual Machine)–负责加载并运行.class字节码文件

  1. 运行过程: 我们编写的源码是.java为后缀的,通过编译生成的是.class字节码文件,交给JVM来执行
  2. 跨平台: 只要在不同的操作系统上安装对应的JVM,就可以实现跨平台:一份代码 处处运行
    在这里插入图片描述

2.语法基础

1.关键字

50个全小写的单词,在Java有特殊的意义,还包含2个保留字const goto
在这里插入图片描述

2.标识符

字母 数字 下划线 美元符号组成,不能以数字开头,区分大小写,关键字+(true false null)也不可以用作标识符,见名知意
UpperCamelCase大驼峰命名:
每个单词的首字母都要大写,比如类名:HelloWorld类名: Upper驼峰命名:每一个单词的首字母都要大写
LowerCamelCase小驼峰命名:
从第二个单词的首字母才开始大写,比如:方法名:nextLine() 变量名:deptName

3.注释

单行注释 //
多行注释 / * * /
文档注释: /** */
还可以添加一些额外的信息:作者 时间 版本 参数 返回值类型
注释可以注释内容,被注释的内容不执行,所以我们可以利用注释手段对进行分段代码测试

4.变量

  1. 成员变量:类里方法外,类消失,成员变量才会消失
    成员有自己的默认值,可以不手动赋值
  2. 局部变量:方法里/代码块里,当局部代码结束,局部变量也随之释放
  3. 局部变量使用的时候,必须赋值,可以:

声明的时候并且赋值 Cat cat = new Cat();
先声明再赋值 Cat cat; cat = new Cat();
注意:基本类型保存的是值,引用类型保存的是地址值

  1. 变量的就近原则:离谁近 就使用谁
  1. 如果想指定本类的成员变量,使用this.变量名来指定
  2. 如果想指定父类的成员变量,使用super.变量名来指定

5.八大基本类型

Java的数据类型分为两类:基本类型 + 引用类型
在这里插入图片描述

6.字面值规则:

  1. 整数类型的字面值类型是int
  2. 浮点类型的字面值类型是double
  3. byte short char 三种比int小的类型,可以在范围内直接赋值
  4. 三种字面值后缀 : L D F
  5. 三种字面值前缀: 0b-二进制 0-八进制 0x-十六进制
  6. 练习:查看字面值前缀
package cn.tedu.basic;
/*本类用于测试字面值前缀*/
public class TestTypePre {
    
    
    public static void main(String[] args) {
    
    
        System.out.println(100);//100,十进制,10的平方
        System.out.println(0b100);//4,二进制,2的平方
        System.out.println(0100);//64,八进制,8的平方
        System.out.println(0x100);//256,十六进制,16的平方

        System.out.println(101);//101=100+1,十进制,10的平方+10的0次方
        System.out.println(0b110);//6=4+2,二进制,2的平方+2的1次方
        System.out.println(0111);//73=64+8+1,八进制,8的平方+8的1次方+8的0次方
        System.out.println(0x101);//257=256+1,十六进制,16的平方+16的0次方
    }
}

7.运算规则:

  1. 运算结果的数据类型与参与运算的最大类型保持一致 int+int->int double/int->double
  2. 整数运算会出现溢出的现象,一旦溢出,数据就不正确了(光年案例)
  3. byte short char三种比int小的类型,运算的时候需要先自动提升int类型,再参与运算
  4. 浮点数的特殊值:Infinity NaN
  5. 浮点数运算不精确的问题:解决方案:BigDecimal
    注意1:不能使用double类型参数的构造方法,需要使用String类型的构造函数,否则还会出现不精确的问题
    注意2:除法运算时,如果出现除不尽的现象还会报错,所以需要指定保留位数与舍入方式(四舍五入)

8.类型转换

口诀:小转大,直接转 大转小,强制转 浮变整,小数没

  1. 注意:布尔类型不参与类型转换
  2. 注意:基本类型之间能否转换,不取决于字节数,字节数只能做参考,取决于类型的取值范围
  3. 注意:我们这里所说的是基本类型之间的转换,引用类型之间的转换取决于是否有继承关系
    比如:你可以说小猫是小动物,但是不能说小猫是小汽车,不然后面的这种错误的情况会报:类型转换异常
    在这里插入图片描述
  4. 练习题:类型之间的转换与字面值规则
package cn.tedu.basic;
/*本类用于测试类型转换
* 1.byte1--short2--char2--int4--long8--float4--double8
* */
public class TestTypeChange {
    
    
    public static void main(String[] args) {
    
    
        byte a = 10;
        short b = a;//不会报错,小转大

        int c = 1;
        long d = c;//不会报错,小转大

        float f = 3.14f;
        double e = f;//不会报错,小转大

        long g = 97525205273520L;
        float h = g;//不会报错,小转大

        char i = 'a';
        int j = i;//不会报错,小转大
        System.out.println(j);//97

        int a1 = 1;
        byte b1 = 2;
        //byte c1 = a1 + b1;//会报错,大转小
        byte c1 = (byte) (a1 + b1);//强制类型转换就不会报错了
        System.out.println(c1);//3

        byte d1 = (byte) 128;
        System.out.println(d1);//-128,强制类型转换要在小类型的范围内转,否则会发生数据溢出的问题

        short e1 = 'a';
        char f1 = 120;
        System.out.println(e1);//97
        System.out.println(f1);//'x'

        float h1 = 32874.456f;
        int i1 = (int) h1;
        System.out.println(i1);//32874

    }
}

9.运算符

  1. 普通的四则运算符 + - * / ,普通的四则运算,并不能直接改变变量本身的值,除非 i = i*10+8
  2. 取余 % 6%4=2 6%3=0(余数为0表示整除)
  3. 自增自减运算符
    1)可以改变变量本身的值
    2)前缀式: 符号在前,先改变变量本身的值(+1/-1),再使用(打印/参与运算…)
    3)后缀式: 符号在后,先使用(打印/参与运算…),再改变变量本身的值(+1/-1)
    4)注意:不管是前缀式还是后缀式,一定是会改变变量本身的值,区别在于执行的时机不同
  4. 比较运算符
    1. 比较运算符最终的结果是布尔类型的
    2. == 比较的是左右两边的值是否相等 !=比较的是左右两边的值是否不相等
  5. 练习题:==比较的练习
package cn.tedu.basic;
/*本类用于测试运算符*/
public class TestOperator {
    
    
    public static void main(String[] args) {
    
    
        //创建小猫类的对象
        Cat c1 = new Cat();
        Cat c2 = new Cat();
        int[] a1 = {
    
    1,2,3};
        int[] a2 = {
    
    1,2,3};
        int b1 = 4;
        int b2 = 4;
        boolean f1 = true;
        boolean f2 = true;
        /*==如果比较的是引用类型,比较的值是引用类型变量保存的地址值*/
        System.out.println(c1 == c2);//false
        System.out.println(a1 == a2);//false
        /*==如果比较的是基本类型,比较的值就是字面值,也就是这个变量具体存的那个数*/
        System.out.println(b1 == b2);//true
        System.out.println(f1 == f2);//true
    }
}

class Cat{
    
    
    String name;
    int age;

    public void bark(){
    
    
        System.out.println("喵喵叫");
    }
}
  1. 逻辑运算符
    双与/短路与/&& :
    判断逻辑与&一致,增加了短路的功能全真才真,有假则假
    双或/短路或/|| :
    判断逻辑与|一致,增加了短路的功能全假才假,有真则真
    注意:我们这里所说的短路,是指在某些情况下,表达式后半部分就不用计算了,因为我们已经知道了结果,也就是被短路了,短路可以提高程序的性能,但是短路不一定会用到
  2. 三目运算符: 1 ? 2 : 3; 1是表达式,1真取2,1假取3
  3. 复合赋值运算符:+= -= *= /=是一种简写的形式,比较方便,运算时会自动进行类型转换
  4. 赋值运算符: = ,右边给左边
  5. 拼接功能:+
  6. 位运算符 : 主要参与的是二进制的运算
    &与:全真才真
    | 或:全假才假
    ^异或:相同为0 不同为1
    ~ 非: 非0为1,非1为0
  7. 优先级控制:如果表达式的运算比较复杂,需要控制优先级,可以使用小括号
  8. 拓展:instanceof

10.流程控制

1.顺序结构

顺序结构中的代码会按照顺序一行一行向下执行所有的代码语句,可以用来进行输入 输出 计算等的操作
但顺序结构不可以完成先做判断,再做选择的流程

2.分支结构

  1. 单分支结构
if(判断条件){
    
    
            如果判断条件的结果为true,就执行此处代码,不符合条件,此处跳过
 }
  1. 多分支结构
if(判断条件){
    
    
    如果判断条件的结果为true,就执行此处的代码
}else{
    
    
    如果不符合条件,执行else处的代码
}
  1. 嵌套分支结构
if(判断条件1){
    
    
    符合判断条件1,执行此处代码,不符合,继续向下判断
}else if(判断条件2){
    
    
    符合判断条件2,执行此处代码,不符合,继续向下判断
}else if(判断条件3){
    
    
    符合判断条件3,执行此处代码,不符合,继续向下判断
}else{
    
    
    保底选项,以上条件均不符合的情况下,执行此处代码
}

4.练习:

package cn.tedu.basic;

import java.util.Scanner;

/*本类用于复习分支结构*/
public class TestIf {
    
    
    public static void main(String[] args) {
    
    
        //1.提示并接收用户输入的月份
        System.out.println("请输入您要测试的月份:");
        int month = new Scanner(System.in).nextInt();

        //2.对用户输入的数据进行判断
        if(month<=0 || month >12){
    
    
            System.out.println("您输入的数据不正确!");
        }else{
    
    
            //3.如果用户输入的数据正确,我们就进行季节的判断
            if(month >=3 && month <=5){
    
    
                System.out.println(month+"月是春天");
            }else if(month >=6 && month <=8){
    
    
                System.out.println(month+"月是夏天");
            }else if(month >=9 && month<=11){
    
    
                System.out.println(month+"月是秋天");
            }else{
    
    
                System.out.println("冬天就要来啦,春天还会远吗?");
            }
        }
    }
}

3.选择结构

  1. 小括号中变量支持的类型:
    byte short char int String enum 与4个基本类型对应的包装类
  2. 注意: 如果配置了default默认选项,而且没有任何的case被匹配到,就会执行default这个“保底选项”
  3. case的个数 是否加break 是否加default全部都是可选的,根据自己的具体业务做决定
  4. 小括号中变量的类型必须与case后value的类型一致
  5. 执行顺序:先拿着变量的值,依次与每个case后的值做比较,如果相等,就执行case后的语句
    若这个case后没有break,就会继续向下执行下一个case,如果一直没有遇到break,就会发生穿透现象,包括default
switch (变量名){
    
    
            case value1 : 操作1;break;//可选
            case value2 : 操作2;break;//可选
            case value3 : 操作3;break;//可选
            case value4 : 操作4;break;//可选
            default:保底选项;//可选
        }

4.循环结构

可以帮我们多次重复的做某一件事
1.for循环

for(开始条件;循环条件;更改条件){
    
    
		如果符合循环条件,就会执行循环体里的内容
}

注意1:写法小窍门:从哪开始 到哪结束 循环变量如何变化
注意2:for循环能够执行多少次,取决于循环变量可以取到几个值
2.嵌套for循环
外层循环控制的是轮数,内层循环控制的是每一轮中执行的次数
对于图形而言,外层循环控制的是行数,内层循环控制的是列数

for(开始条件;循环条件;更改条件){
    
    //外层循环
		for(开始条件;循环条件;更改条件){
    
    //内层循环
				循环体
		}
}

注意:外层循环控制的是行数,内层循环控制的是列数
注意:外层循环控制的是轮数,内层循环控制的是在这一轮中执行的次数
3.高效for循环

for(遍历到的元素的类型 遍历到的元素的名字 :要遍历的数组/集合名){
    
    
		循环体
}

优点:写法简单,效率高
缺点:只能从头到尾的遍历数据,不能进行步长的选择
4.while循环

while(判断条件){
    
    
		如果符合判断条件,继续循环
}

注意:常用来完成死循环,但死循环必须设置出口!
练习题:while循环练习

package cn.tedu.basic;
/*本类用于复习while循环*/
public class TestWhile {
    
    
    public static void main(String[] args) {
    
    
        //需求:通过while循环打印10次"小可爱们中午好~"
        //f1();
        //需求:通过while循环打印1 2 3 ... 10
        //f2();
        //需求:通过while循环打印1 3 5 7... 99
        f3();
        //需求:通过while计算1+2+3+4...+10
        f4();
        //需求:通过while计算2+4+6+8...+100
        f5();
    }

    private static void f5() {
    
    
        //需求:通过while计算2+4+6+8...+100
        int i = 2;//定义循环变量i用来控制循环
        int sum = 0;//定义变量用来保存累加的结果
        while (i<=100){
    
    
            //sum += i;
            sum = sum + i;//累加
            //i += 2;
            i = i + 2;//循环变量i每次+2
        }
        System.out.println("2到100所有偶数的和为:"+sum);
    }

    private static void f4() {
    
    
        //需求:通过while计算1+2+3+4...+10
        int i = 1;//用于控制循环,相当于循环变量
        int sum = 0;//用于保存求和累加的结果
        while(i<=10){
    
    
            sum = sum + i;
            i++;
        }
        System.out.println("1到10累加的结果为:"+sum);
    }

    private static void f3() {
    
    
        //需求:通过while循环打印1 3 5 7... 99
        int sum = 1;
        while(sum <100){
    
    
            System.out.println(sum);
            //sum = sum +2;
            sum += 2;
        }
    }

    private static void f2() {
    
    
        //需求:通过while循环打印1 2 3 ... 10
        //1.定义一个变量来控制执行的次数
        int i = 1;
        while(i<=10){
    
    
            System.out.println(i);
            i++;
        }
    }

    private static void f1() {
    
    
        //需求:通过while循环打印10次"小可爱们中午好~"
        //1.定义一个变量用来控制执行的次数
        int count = 1;
        while(count <= 10){
    
    
            System.out.println("小可爱们中午好~");
            count++;//注意count的值需要自增,否则还是一个死循环
        }
    }
}

5.do-while循环
do-while循环一定会执行一次,然后再判断,如果符合条件,再执行后面的循环

do{
    
    
		循环体
}while(判断条件);

循环之间的比较

  1. 如果明确知道循环的次数/需要设置循环变量的变化情况时–使用for循环
  2. 如果想写死循环–while(true){}
  3. 如果需要先执行一次,再做判断–do-while循环
  4. 循环之间是可以互相替换的,但是最好使用比较合适的循环结构

11. 方法

  1. 格式: 修饰符 返回值类型 方法名(参数列表){ 方法体 }
  2. 如何确定我们要调用哪个方法呢?方法名+参数列表
  3. 一个方法被调用,就会执行这个方法的功能,执行完毕就返回到方法被调用的位置,在第几行调用,程序就返回到第几行继续向下执行
    如果这个方法有返回值,我们有两种选择:
    1. 选择只执行功能,不接收返回值,不再继续使用这个方法的结果
    2. 选择在方法调用的位置接收这个方法的返回值,接收到的返回值可以在方法外继续使用在这里插入图片描述
  4. 方法的重载:
    在同一个类中出现多个方法名相同,但参数列表不同的方法
    注意:方法是否构成重载,取决于参数列表中参数的个数与参数的类型,与参数的名字无关
    重载的意义: 重载不是为了程序员方便,而是为了方便外界调用这个名字的方法时,不管传入什么类型的参数,都可以匹配到对应的方法来执行,程序会更加的灵活
  5. 方法的传值 : 如果方法的参数类型是基本类型,传入的是实际的字面值,如果是引用类型,传入的是地址值
    形参:形式意义上的参数,比如方法参数列表的参数名,光看参数名是无法确定这个变量的值是多少的
    实参:实际意义上的参数,比如我们的局部变量,比如成员变量,比如调用方法时传入的数字
  6. 方法的重写: 子类继承了父类以后,想要在不改变父类代码的情况下,实现功能的修改与拓展
    重写遵循的规则:两同 两小 一大
    一大: 子类方法的修饰符权限 >= 父类方法的修饰符权限
    两同: 方法名与参数列表与父类方法保持一致
    两小: 子类方法的返回值类型 <= 父类方法的返回值类型,注意:这里的<=说的是继承关系,不是值的大小
    子类方法抛出的异常类型 <= 父类方法抛出的异常类型
  7. 四种权限修饰符
    在这里插入图片描述
    8.练习题:方法调用的顺序
package cn.tedu.basic;
/*本类用于测试方法的调用*/
public class MethodDemo {
    
    
    //1.创建程序的入口函数main()
    public static void main(String[] args) {
    
    
        System.out.println("main() start...");
        m1();
        System.out.println("main() stop...");
        int a =5;
        System.out.println(a);
    }

    //2.创建m1()
    private static void m1() {
    
    
        System.out.println("m1() start...");
        m2();
        System.out.println("m1() stop...");
    }
    private static void m2() {
    
    
        System.out.println("m2() start...");
    }
}

9.拓展 : 方法的递归

12. 数组

  1. 数组的创建方式:
    静态创建 int[] a = {1,2,3,4,5};
    静态创建 int[] a = new int[]{1,2,3,4,5};
    动态创建 int[] a = new int[5]; 后续可以动态的给数组中的元素赋值
    注意:不管是什么样的创建方式,都需要指定数组的类型与长度
  2. 我们可以通过数组的下标来操作数组中的元素
    数组下标从0开始,最大下标是数组的长度-1,如果访问到了不是这个数组的下标,会出现数组下标越界异常
    比如:a[5]表示的就是数组中的第6个元素
  3. 数组的长度:数组一旦创建,长度不可改变,长度指的是数组中元素的个数a.length,并且数组的长度允许为0:[ ]
  4. 数组的创建过程:
    1. 根据数组的类型与长度开辟一块连续的内存空间
    2. 对数组中的每个元素进行初始化,比如int数组的默认值就是0
    3. 生成数组唯一的一个地址值,交给应用类型变量a来保存
    4. 后续可以根据数组的下标再来操作数组中的具体元素
      注意: 数组名a这个变量,保存的是数组的地址,不是数组中的具体元素
      在这里插入图片描述
  5. 数组的工具类Arrays
    1. toString(数组名) : 除了char类型的数组以外,其他类型的数组想要查看具体元素,需要使用本方法,否则打印的是地址值
    2. copyOf(原数组名,新数组的长度) : 用来实现数组的复制 扩容 缩容
      如果新数组的长度 > 原数组长度,就扩容,反之,则缩容,如果两者长度一致,就是普通的复制数组
      注意:一定是创建了新数组,而不是对原数组的长度做操作
  6. 数组的遍历
    一般习惯使用for循环,循环变量代表的就是数组的下标,从0开始,最大值是a.length-1
  7. 冒泡排序 :
    外层循环控制比较的轮数,所以循环变量从1到n-1轮,代表的是轮数
    内层循环控制的是这一轮中比较的次数,而且是数组中两个相邻元素的比较,所以循环变量代表的是数组的下标
    8.练习:
package cn.tedu.basic;
/*本类用于复习数组的操作*/
public class TestArray {
    
    
    public static void main(String[] args) {
    
    
        //需求1:求出数组中所有元素之和
        //f1();
        //需求2:求出数组中所有元素的最大值
        f2();
    }

    private static void f2() {
    
    
        //需求2:求出数组中所有元素的最大值
        //1.定义一个数组
        int[] a = {
    
    10,20,98,40,55};
        //2.定义一个变量,用来存储结果,也就是数组中所有元素的最大值
        int max = a[0];//这个位置不能写0,应该是数组中的第一个元素
        //3.遍历数组,依次拿出数组中的每一个元素与之前设定的元素做比较
        for (int i = 0; i < a.length; i++) {
    
    
            //4.判断当前遍历到的元素,与max谁大
            if(a[i]>max){
    
    //如果当前遍历到的元素比max大
                max = a[i];//就把遍历到的这个元素的值赋值给max
            }
        }
        //5.循环结束,输出数组中最大的值
        System.out.println("数组中的最大值为:"+max);
    }

    private static void f1() {
    
    
        //需求1:求出数组中所有元素之和
        //1.定义一个数组
        int[] a = {
    
    12,12,22,22,22};
        //2.定义一个变量,用来保存最终求和的结果
        int sum = 0;
        //3.通过数组的遍历,遍历数组中的每一个元素,然后累加
        for (int i = 0; i < a.length; i++) {
    
    
            sum = sum + a[i];
        }
        System.out.println("数组累加所有元素之和为:"+sum);
    }
}

2 面向对象OOP

1.面向对象与面向过程

  1. 两者都是一种编程的思想
  2. 面向对象强调的是事情的结果,我们通过对象完成对应的功能
  3. 面向过程强调的是事情的过程,我们做任何事情,都要亲力亲为,经过每一个步骤
  4. Java是一门面向对象的语言

2.类与对象

  1. 定义类通过关键字class来定义,类是一类事物的抽象,它是抽象的,它是模板
  2. 创建对象通过new关键字触发构造函数生成,对象是根据类创建出来的具体的内容
  3. 一个类可以创建出多个对象,对象是根据类的设计来创建的,所以对象具有类的所有属性与功能
  4. 对象之间是相互独立的,互不影响。我们把创建对象也称作“实例化”

3.面向对象的三大特性:封装

  1. 前提:为了保证数据的安全,也为了程序的使用者能够按照我们预先设计好的方式来使用资源
  2. 封装属性:用private修饰我们的属性
    然后为属性提供对应的getXxx()【获取属性值】与setXxx()【设置属性值】
  3. 封装方法:用private修饰方法,被修饰的方法只能在本类中使用,所以我们在本类的公共方法里调用这个私有方法
    外界如果想要使用这个私有方法的功能,只需要调用这个公共方法就可以了

4.面向对象的三大特性:继承

  1. 前提 :继承可以实现程序的复用性,减少代码的冗余
  2. 我们通过extends关键字建立子类与父类的继承关系:格式:子类 extends 父类
  3. 继承相当于子类把父类的功能复制了一份,包括私有资源
    注意:虽然私有资源继承了,但是私有资源不可用,原因是被private限制了访问,私有资源只能在本类使用
    注意:构造方法不能继承,原因是:构造方法要求名字是本类的类名,我们不能在子类中出现父类名字的构造方法
  4. 继承是可以传递的:爷爷的功能会传给爸爸,爸爸的功能会传给孙子
    注意:爸爸从爷爷那里继承的功能,也会一起传给孙子
  5. Java的类是单继承的:一个子类只能有一个父类,但是一个父类可以有多个子类
  6. 子类在继承了父类以后,如果对父类的功能不满意
    可以在不修改父类功能的【满足OCP原则】前提下,在子类中,重写继承过来的这个方法
    重写需要满足的规则:两同 两小 一大,我们可以在重写的方法上加@Override注解验证是否写对
  7. 继承是一种is a的关系,强耦合,关联性特别强,而且类的继承机会只有一次,要谨慎使用
  8. 子类可以直接使用父类的所有非私有资源
    练习题:
package cn.tedu.oop;

import java.util.Random;
import java.util.Scanner;

/*本类用于复习面向对象的相关知识*/
public class TestOOP {
    
    
    public static void main(String[] args) {
    
    
        //8.提示并接收用户传入的宠物昵称
        System.out.println("请您输入宠物的名字:");
        String name = new Scanner(System.in).nextLine();
        //9.触发对应的构造函数,创建小猫类的对象
        Cat c = new Cat(name);
        //10.与宠物做互动
        System.out.println("按回车执行:");
        while(true){
    
    
            new Scanner(System.in).nextLine();
            int r = new Random().nextInt(6);
            switch (r){
    
    
                case 0:c.feed();break;
                case 1:c.play();break;
                default:c.punish();break;
            }
        }

    }
}
//1.创建父类:宠物类,描述宠物这一类事物
class Pet{
    
    
    //2.定义宠物类的属性
    String name;//电子宠物的姓名
    int full;//饱食度
    int happy;//快乐度

    //3.1 生成本类的全参构造
    public Pet(String name, int full, int happy) {
    
    
        this.name = name;
        this.full = full;
        this.happy = happy;
    }
    //3.2 创建本类的含参构造
    public Pet(String name){
    
    
        //只需要用户起宠物名即可,饱食度与快乐度的初始值都是50
        //实际上底层会触发全参构造,名字是用户起的,其他两个是预先设置好的默认值
        this(name,50,50);
    }
    //4.1定义宠物类的功能1--喂宠物吃东西
    public void feed(){
    
    
        //先判断宠物是不是吃饱了,如果吃饱了,结束整个喂食方法
        if(full == 100){
    
    
            System.out.println(name+"已经吃饱啦~");
            return;//遇到return关键字,整个方法直接结束
        }
        //如果没有吃饱,就给宠物喂食,一次增加10的饱食度
        System.out.println("给"+name+"喂食:");
        full = full + 10;
        System.out.println("饱食度:"+full);
    }
    //4.2定义宠物类的功能2--陪宠物玩
    public void play(){
    
    
        //先判断宠物还有没有饱食度,如果没有饱食度了,就不能玩了,结束整个玩的方法
        if(full == 0){
    
    
            System.out.println(name+"已经饿的玩不动了...");
            return;//遇到return关键字,整个方法直接结束
        }
        //如果饱食度不为0,就可以玩耍,每次玩耍快乐度+10,饱食度-10
        System.out.println("陪"+name+"玩耍");
        happy += 10;
        full -= 10;
        System.out.println("快乐度:"+happy);
        System.out.println("饱食度:"+full);
    }
    //4.3定义宠物类的功能3--宠物的惩罚方法
    public void punish(){
    
    
        System.out.println("打"+name+"的PP,哭叫声:"+cry());
        happy -= 10;
        System.out.println("快乐度:"+happy);
    }
    //4.4定义一个哭的方法--宠物被打哭了
    public String cry(){
    
    
        return "此处有哭叫声";//这个位置没有明确的叫声,因为子类会重写
    }
}

//5.创建子类小猫类
class Cat extends Pet{
    
    
    //6.由于父类的无参构造已经没有了,所以这里要手动调用父类的含参构造
    public Cat(String name) {
    
    //养猫的时候要给猫起昵称
        super(name);//表示调用父类的含参构造
    }
    //7.重写父类的cry();
    @Override
    public String cry(){
    
    
        return "喵~";
    }
}

5.面向对象的三大特性:多态

  1. 前提:为了忽略子类型之间的差异,统一看作父类类型,写出更加通用的代码
    比如:把Cat看作Animal,把Dog看作Animal,把Bird看作Animal,如果方法需要设置传入的参数,可以buy(Animal a)
    比如:把算术异常、输入不匹配异常都看作是Exception,统一捕获处理,只写一个解决方案
  2. 概念:在同一时刻,同一个对象,代表的类型不同,拥有多种形态
  3. 多态的要求:继承 + 重写
  4. 多态的口诀1:父类引用指向子类对象:父类型的引用类型变量保存的是子类对象的地址值
  5. 多态的口诀2:编译看左边,运行看右边:
    父类中定义的功能,子类才能使用,否则报错
    多态中,方法的定义看的是父类的,方法的实现看的是子类重写后的功能
    在这里插入图片描述
  6. 多态中资源的使用:
    1)成员变量:使用的是父类的
    2)成员方法:对于方法的定义看的都是父类的,对于方法实现,重写后使用的是子类的
    3)静态资源:静态资源属于类资源,不存在重写的概念,在哪个类中定义的,就属于哪个类
  7. 向上造型与向下造型
    1)这两种都属于多态,只不过是多态的两种不同的表现形式
    2)向上造型【最常用】
    可以把不同的子类型都看作是父类型,比如Parent p = new Child();
    比如:花木兰替父从军,被看作是父类型,并且花木兰在从军的时候,不能使用自己的特有功能,比如化妆
    3)向下造型
    前提:必须得先向上造型,才能向下造型
    子类的引用指向子类的对象,但是这个子类对象之前被看作是父类类型,所以需要强制类型转换
    Parent p = new Child(); 然后:Child c = (Child) p;
    比如:花木兰已经替她爸打完仗了,想回家织布,那么这个时候,一直被看作是父类型的花木兰必须经历“解甲归田”【强制类型转换】这个过程,才能重新被看作成子类类型,使用子类的特有功能
    为什么有向下造型:之前被看作是父类类型的子类对象,想使用子类的特有功能,那就需要向下造型

6.构造方法

  1. 格式 :修饰符 类名当做方法名(){ } 注意:与类同名且没有返回值类型
  2. 构造方法作用:用于创建对象,每次new对象时,都会触发对应的构造函数,new几次,触发几次
  3. 一个类中默认存在无参构造,如果这个构造不被覆盖的话,我们可以不传参数,直接创建这个类的对象
  4. 如果这个类中提供了其他的构造函数,默认的无参构造会被覆盖,所以记得手动添加无参构
  5. 构造方法也存在重载的现象:无参构造 含参构造 全参构造【创建对象+给所有的属性赋值】

7.this与super

  1. this代表的是本类,super代表的是父类
  2. 当本类的成员变量与局部变量同名时,我们可以通过this.变量名指定本类的成员变量
    当父类的成员变量与子类的变量同名时,我们可以通过super.变量名指定父类的成员变量
  3. 我们可以在本类构造函数的第一行
    使用this();调用本类的无参构造 / 使用this(参数); 调用本类对应参数的构造方法
    构造函数的调用只有这一种方式,或者创建对象时被动触发,不能在外面自己主动调用
    构造函数直接不能互相调用,否则会死循环
  4. 我们可以在子类构造函数的第一行
    使用super();调用父类的无参构造 / 使用super(参数); 调用父类对应参数的构造方法
    注意:子类默认调用super();父类的无参构造,如果父类没有无参构造,需要手动指定调用哪个含参构造

8.对象创建的过程

  1. 前提:对象是根据类的设定来创建的,目前我们可以在类中添加很多的元素:
    属性 方法 静态方法 构造代码块 静态代码块 局部代码块 构造方法…所以不限制类里具体写什么,取决于业务
  2. 对象创建的过程:Phone p = new Phone();
    1. 需要在堆内存中开辟一块空间,用来存放对象
    2. 对象需要完成初始化,比如对应的属性都有自己的对应类型的默认值
    3. 对象创建完毕后,会生成一个唯一的地址值用于区分不同的对象
    4. 将这个地址值交给引用类型变量来保存
    5. 后续如果想要使用这个类的功能,可以从引用类型变量中保存的地址值找到对应的对象做进一步的操作
  3. 匿名对象:new Phone();
    匿名对象是没有名字的对象,所以创建过程:
    1. 需要在堆内存中开辟一块空间,用来存放对象
    2. 对象需要完成初始化,比如对应的属性都有自己的对应类型的默认值
    3. 对象创建完毕后,会生成一个唯一的地址值用于区分不同的对象
      那么我们使用匿名对象只能使用一次,并且一次只能使用一个功能
      new Phone().video();//创建匿名对象1,调用看直播的方法
      new Phone().message();//创建匿名对象2,调用看发短信的方法

9.代码块与它们的执行顺序

1.静态代码块 static { }

位置:类里方法外
执行时机:随着类的加载而加载,最先加载到内存,优先于对象进行加载,直到类小消失,它才会消失
作用:一般用来加载那些只需要加载一次并且第一时间就需要加载资源,称作:初始化

2.构造代码块 { }

位置:类里方法外
执行时机:创建对象时执行,创建几次,执行几次,并且优先于构造方法执行
作用:用于提取所有构造方法的共性功能

3.局部代码块 { }

位置:方法里
执行时机:当其所处的方法被调用时才会执行
作用:用于限制变量的作用范围,出了局部代码块就失效

4.代码块之间的顺序:

静态代码块 -> 构造代码块 -> 构造方法 -> 普通方法【如果普通方法里有局部代码块,局部代码块才会执行】

10 static

  1. 被static修饰的资源统称为静态资源,可以用来修饰变量、方法、代码块、内部类
  2. 静态资源属于类资源,随着类的加载而加载,优先于对象进行加载,只加载一次
  3. 静态资源可以不通过对象,使用类名直接调用,不需要创建对象
  4. 静态资源只有一份,被全局所有对象共享
  5. 静态的调用关系:静态资源只能调用静态资源
  6. 静态资源是优先于对象的,所以静态资源不能与this和super共用

11 final

  1. final表示最终
  2. 被final修饰的类是最终类,也称作叶子结点,所以不能被继承
  3. 被final修饰的方法是这个方法的最终实现,不能被重写
  4. 被final修饰的是常量,值不可以被修改,注意常量定义时必须赋值

12 抽象

  1. 抽象的关键字是abstract
  2. 被abstract修饰的方法是抽象方法,抽象方法没有方法体
  3. 如果一个类中出现了一个抽象方法,那么这个类必须被abstract修饰
  4. 关于抽象类的特点:
    1)抽象类中的方法不做限制 : 全普 / 全抽 / 半普半抽
    2)如果一个类中的方法都是普通方法,还要声明成抽象类,为什么?
    为了不让外界创建本类的对象
    3)抽象类不可以创建对象,所以常用于多态
    4)抽象类中包含构造方法,但是不是为了自己创建对象时使用,而是为了子类的super()
    5)抽象类中也是可以定义成员变量的
  5. 如果一个子类继承了一个抽象父类,有两种解决方案:
    1)作为抽象子类:不实现/实现部分 抽象父类中的抽象方法 : ”躺平”
    2)作为普通子类:实现抽象父类中的所有的抽象方法 : “父债子偿”
  6. 面向抽象进行编程:后天重构的结果

13 接口

  1. 接口不是类,定义接口的关键字interface
  2. 如果一个类想要实现接口中定义的规则,需要使用implments与接口建立实现关系
    注意:如果有任何一个抽象方法没有实现,那么这个类就是一个抽象子类
  3. Java8中接口里的所有方法都是抽象方法
  4. 接口中只有静态常量,没有普通变量,会自动拼接public static final
  5. 接口中的方法也可以简写,会自动拼接public abstract
  6. 接口不可以实例化
  7. 接口中也没有构造方法,实现类调用的是它自己父类的构造方法,如果没有明确指定父类,那就是Object的
  8. 接口更多的是规则的制定者,不做具体的实现
  9. 接口降低了程序的耦合性,更加方便项目的维护与拓展
  10. 接口是先天设计的结果,这样可以省去后续的多次重构,节省资源

14 接口与类的复杂关系

1.类与类的关系

Java的类只支持单继承,类与类就是继承关系,并且一个子类只能有一个父类
class Son extends Father{ }

2.接口与接口的关系

Java的接口是不做限制的,可以多继承
interface Inter1 extends Inter2{ } – Inter1是子接口 Inter2 是父接口
interface Inter1 extends Inter2,Inter3{ } – Inter1 是子接口 Inter2 和 Inter3 都是父接口
注意:如果是情况2的话,接口1的实现类需要实现这三个接口(Inter1,2,3)的所有抽象方法

3.接口与类的关系

Java中的类对于接口而言是多实现的,所以一个类可以实现多个接口
class InterImpl implements Inter1{}
class InterImpl implements Inter1,Inter2{}

4.接口与抽象类的区别

  1. 接口是一种用interface定义的类型
    抽象类是一种用class定义的类型
  2. 接口中的方法都是抽象方法,还有默认方法与静态方法
    抽象类中的方法不做限制
  3. 接口中的都是静态常量
    抽象类中可以写普通的成员变量
  4. 接口没有构造方法,不可实例化
    抽象类有构造方法,但是也不可以实例化
  5. 接口是先天设计的结果,抽象是后天重构的结果
  6. 接口可以多继承
    抽象类只能单继承

15 异常

  1. 异常的继承结构
    异常层次结构中的根是Throwable
    Error:目前我们编码解决不了的问题
    Exception:异常
    编译异常:未运行代码就报错了,强制要求处理
    运行时异常RunTimeException:运行代码才报错,可以通过编译,不强制要求处理
  2. 异常的解决方案
  3. 捕获处理try-catch–自己解决
  4. 格式:
try{
    
    
	可能会出现异常的代码
}catch(预测的异常类型 异常的名字){
    
    
	预先设计的,捕获到异常的处理方案
}finally{
    
    
	异常处理结构中一定会被执行到的代码块,常用来关流
}
  1. 向上抛出throws–交给别人解决,在方法定义的两个小括号之间throws,可抛出多个异常,用逗号隔开
  2. 不能直接把异常抛给main(),因为调用main()是JVM,没人解决了
    注意:是否抛出异常取决于自己的业务,比如暂时不处理或者处理不了需要交给别人处理
    在这里插入图片描述

16 内部类

  1. 我们可以把内部类看作是外部类的一个特殊的资源
  2. 内部类可以直接使用外部类的所有资源,包括私有资源
  3. 外部类如果想要使用内部类的资源,需要创建内部类的对象才能使用
  4. 对象的普通创建方式:
 /*外部类名.内部类名 对象名 = 外部类对象.内部类对象*/
Outer.Inner oi = new Outer().new Inner();

成员内部类
位置:类里方法外
1)被private修饰
被私有化的内部类在main()中是没有办法直接创建其对象的
可以在私有内部类所处的外部类中,创建一个公共的方法供外界调用,这个方法用来返回创建好的私有内部类对象
2) 被static修饰
静态内部类可以不创建外部类对象,直接创建静态内部类对象,格式:Outer3.Inner3 oi = new Outer3.Inner3();
如果静态内部类中还有静态方法,那么我们可以不创建对象
直接通过链式加载的方式调用:Outer3.Inner3.show2();//表示通过外部类名直接找到静态内部类,再找到静态方法
局部内部类
位置:方法里
直接创建外部类对象,调用局部内部类所处的方法,并不会触发局部内部类的功能
需要在外部类中创建局部内部类的对象并且进行调用局部内部类的功能,才能触发内部类的功能
匿名内部类
位置:可运行代码中,比如 main()中
匿名内部类通常与匿名对象【没有名字的对象】一起使用
格式:new Inter1(){ 我这个大括号其实是一个匿名内部类,我来实现方法 }.eat();
如果只是想使用一次接口/抽象类的某个功能,可以使用匿名内部类
匿名内部类+匿名对象的功能:创建实现类+实现方法+方法功能的一次调用【功能三合一】

3 基础API

1. Object

  1. 是所有类的超类,Java中的类都直接或者间接的继承了Object
  2. 如果一个类没有明确指定父类,那么默认继承Object
  3. Object处于java.lang包之下,不需要导包可以直接使用
  4. toString()–我们日常使用最频繁的打印语句底层就调用了这个方法
    如果没有重写这个方法,使用的是Object的默认实现,打印的是对象的地址值
    如果重写以后,以重写的逻辑为准,比如String打印的是串的具体内容,比如ArrayList,打印的是[集合元素]
  5. hashCode()–用于返回对象对应的哈希码值
    如果是一个对象多次调用这个方法,返回的是同一个哈希码值
    如果是不同的对象调用这个方法,应该返回的是不同的哈希码值
  6. equals()–用于比较当前对象与参数对象是否相等
    重写之前的默认实现比较的是两个对象的地址值
    重写之后取决于重写的逻辑,比如String比较的是两个串的具体内容,比如自定义对象比较的是类型+属性值
  7. equals()与hashCode()应该保持一致【要重写都重写】
    解释:equals()底层默认实现比较的是==比较,地址值,重写后我们一般比较的是对象的类型+属性值
    hashCode()不同的对象生成的哈希码值不同,那么与equals()的逻辑不匹配,所以也应该重写
    重写后,是根据对象的类型与属性值来生成哈希码值,这样二者就一致了

2. String

  1. String底层维护的是一个char[],而且String不可变,因为源码中的数组被final修饰了
  2. 创建方式:
    char[] vlaues = {‘a’,‘b’,‘c’}; String s = new String(values);
    String s = “abc”;有高效的效果,因为串存在堆中的常量池,第二次使用时就不再新建了
  3. 常用方法:

int hashCode() 返回此字符串的哈希码。
boolean equals(Object anObject) 将此字符串与指定的对象比较,比较的是重写后的串的具体内容
String toString() 返回此对象本身(它已经是一个字符串!)。

int length() 返回此字符串的长度。
String toUpperCase() 所有字符都转换为大写。
String toLowerCase() 所有字符都转换为小写
boolean startsWith(String prefix) 测试此字符串是否以指定的元素开头。
boolean endsWith(String suffix) 测试此字符串是否以指定的字符串结束。

char charAt(int index) 返回指定索引/下标处的 char 值/字符
int indexOf(int ch) 返回指定字符在此字符串中第一次出现处的索引。
int lastIndexOf(int ch) 返回指定字符在此字符串中最后一次出现处的索引。
String concat(String str) 将指定字符串连接/拼接到此字符串的结尾,注意:不会改变原串
String[] split(String regex) 根据给定元素来分隔此字符串。

String trim() 返回去除首尾空格的字符串
byte[] getBytes() 把字符串存储到一个新的 byte 数组中
String substring(int beginIndex) 返回一个新子串,从指定下标处开始,包含指定下标
String substring(int beginIndex, int endIndex) 返回一个新子串,从执定下标开始,到结束下标为止,但不包含结束下标
static String valueOf(int i) 把int转成String

3. StringBuilder与StringBuffer

String的:

  1. 特点:
    创建之后长度内容是不可变的,每次拼接字符串,都会产生新的对象
  2. 优缺点:
    优点:String类提供了丰富的关于操作字符串的方法,比如:拼接、获取对应下标处的字符、截取子串等等
    缺点:在进行字符串拼接+=的时候,效率比较低
  3. String转StringBuilder:
    String s = “abc”; StringBuilder sb = new StringBuilder(s);

StringBuilder的:

  1. 特点:
    StringBuilder是一个长度可变的字符串序列,在创建的时候,会有一个长度为16的默认空间
    当拼接字符串的时候,是在原对象的基础之上进行拼接,如果长度不够就扩容
    所以StringBuilder在创建之后,对应的操作一直是用一个对象
  2. 创建方式:
    StringBuilder sb = new StringBuilder();//创建一个长度为16的StringBuilder对象
    StringBuilder sb = new StringBuilder(“abc”);//以指定字符串内容为“abc”的方式创建一个StringBuilder对象
  3. 优缺点:
    优点:在拼接的时候,不会产生新的对象,就避免了因为拼接频繁生成对象的问题,提高了程序的效率,使用的是append()
    缺点:对于字符串的操作,不太方便
  4. StringBuilder转String:
    StringBuilder sb = new StringBuilder();
    sb.append(“abc”);
    String s = sb.toString();
    总结一句话,拼接多用StringBuilder,用完转回String用String丰富的方法

4. 包装类

  1. 基本类型只存值,也没有丰富的功能
    所以包装类型是对基本类型做了包装,并提供了很多方便的方法,所以包装类的对象是引用类型的对象
  2. 创建方式:
    Integer i1 = Integer.valueOf();数据只要在-128~127有一个高效的效果
    Integer i2 = new Integer(4);没有高效的效果,只是创建了一个包装类的对象

5. 自动装箱与自动拆箱

  1. 自动装箱:
    编译器会自动把基本类型int5,包装成包装类型Integer
    然后交给Integer类型的引用类型变量i3来保存
    自动装底层发生的代码:Integer.valueOf(5)
    valueOf()的方向:int–>Integer
  2. 自动拆箱:
    编译器会自动把包装类型的i1拆掉”箱子“,变回基本类型的数据127
    然后交给基本类型int的变量i4来保存
    自动拆箱底层发生的代码:i1.intValue();
    intValue()的方向:Integer-> int

4 高级API

1. IO流

  1. 学习方式:学习抽象父级的公共方法 学习子类流对象的创建方式
  2. 流的分类
    根据方向:输入流 输出流
    根据操作单位:字节流 字符流
字节输入流InputStream:
InputStream--抽象父类--不能实例化
FileInputStream--文件字节输入流-FIS
BufferedInputStream--高效字节输入流-BIS
	FIS in = new FIS(new File(路径));
	FIS in = new FIS(路径);
	BIS in = new BIS( new FIS(new File(路径)));
	BIS in = new BIS(new FIS(路径));
字节输出流OutputStream:
OutputStream--抽象父类,不能实例化
FileOutputStream--文件字节输出流--FOS
BufferedOutputStream--高效字节输出流-BOS
	FOS out = new FOS(new File(路径));  
	FOS out = new FOS(路径);
	BOS out = new BOS(new FOS(new File(路径))); 
	BOS out = new BOS(new FOS(路径));
字符输入流Reader:
Reader--抽象父类--不能实例化
FileReader--文件字符输入流-FR
BufferedReader--高效字符输入流-BR
	FR in = new FR(new File(路径));
	FR in = new FR(路径);
	BR in = new BR(new FR(new File(路径)))
	BR in = new BR(new FR(路径));
字符输出流Writer:
Writer--抽象父类,不能实例化
FileWriter--文件字符输出流--FW
BufferedWriter--高效字符输出流--BW
	FW out = new FW(File/File,append/String pathname/String pathname,append);
	BW out = new BW(Writer--所以传的是子类FW(上面那4种));
	注意:这里的append参数表示流向文件输出数据的时候是追加还是覆盖,如果不写,默认false是覆盖,如果改为true,表示追加
  1. 序列化与反序列化
    序列化与反序列化的作用就是对象的保存与传输
    序列化:把内存中的对象通过序列化流输出到磁盘中(比如文件里),使用的流是ObjectOutputStream【把数据写出到文件】
    反序列化:通过反序列化流将磁盘中的数据恢复成对象,使用的流是ObjectInputStream【把之前写到文件里的数据读到程序中】
    注意1:一个类的对象如果想被序列化,那么这个类必须实现可序列化接口
    实现这个接口的目的是相当于给这个类做了一个标记,标记可以序列化
    注意2:序列化时会自动生成一个UID,表示当前序列化输出的对象的版本信息
    反序列化时会拿着当前的UID与之前序列化输出的UID做比较,一致,反序列化成功,不一致,报错
    注意3: 所以,标准操作是一次序列化对应一次反序列化
    如果目标对象所在的类没有做任何修改,一次序列化也可以对应多次反序列化(根本原因是UID没变)

2. 集合

  1. 泛型
    泛型通常与集合一起使用,用来约束集合中元素的类型
    泛型< type >必须写引用类型而不是基本类型
    泛型方法 public static == < E > == void get(E[] e){},两处位置都出现了泛型,缺一不可
  2. 集合被称作Collection,是一个可以存放多个数据的容器,而且集合中提供了丰富的方法来操作集合中的元素
    是集合层次的根接口,学习抽象父级的公共方法
  3. Collection集合方法总结

单个集合的操作:

boolean add(E e) 将指定元素添加到集合中
void clear() 清空集合
boolean contains(Object o) 判断本集合是否包含指定的元素
boolean equals(Object o) 比较集合对象与参数对象o是否相等
int hashCode() 返回本集合的哈希码值
boolean isEmpty() 判断本集合是否为空
boolean remove(Object o) 从本集合中移除指定元素o
int size() 返回本集合中元素的个数
Object[] toArray() 将本集合转为数组

集合间的操作:

boolean addAll(Collection<> c) 将c集合中的所有元素添加到本集合中
boolean containsAll(Collection<> c) 判断本集合是否包含c集合的所有元素
boolean removeAll(Collection<> c) 移除本集合中属于参数集合c的所有元素
boolean retainAll(Collection<> c) 保留本集合与参数集合c的公共元素

集合的迭代:

Iterator iterator() 返回本集合的迭代器

  1. List接口的特点
    1. List集合是有下标的
    2. List集合是有顺序的
    3. List集合可以存放重复的数据

单个集合的操作:

void add(int index, E element) 在集合的指定下标index处插入指定元素element
E get(int index) 返回本集合中指定下标index处的元素
E remove(int index) 移除本集合中指定下标index处的元素
E set(int index, E element) 用参数元素element替换集合中指定下标index处的元素
int indexOf(Object o) 判断指定元素o在本集合中第一次出现的下标,如果不存在,返回-1
int lastIndexOf(Object o) 判断指定元素o在本集合中最后一次出现的下标,如果不存在,返回-1
List subList(int fromIndex, int toIndex) 截取子集合,包含formidex处的元素,不包含toIndex处的元素

集合间的操作与集合的迭代

boolean addAll(int index, Collection<> c) 将参数集合c中的所有元素,插入到本集合中指定的下标index处
ListIterator listIterator() 返回此列表元素的迭代器,这个是List自己的,不太常用,可以逆序迭代

  1. List接口的两个常用实现类
    ArrayList的特点:
    1)底层的数据结构是数组,内存空间是连续的
    2)元素有下标,通常可以根据下标进行操作
    3)增删操作比较慢,查询操作比较快【数据量大时】
    LinkedList的特点:
    1)底层的数据结构是链表,内存空间是不连续的
    2)元素有下标,但是通常首尾节点操作比较多
    3)增删操作比较快,查询操作比较慢【数据量大时】
    注意:LinkedList查询慢也不是都慢,首尾操作还是比较快的

简单方法:

void addFirst(E e) 添加首元素
void addLast(E e) 添加尾元素
E removeFirst() 删除首元素
E removeLast() 删除尾元素
E getFirst() 获取首元素
E getLast() 获取尾元素
E element() 获取首元素

功能一致但是名字不太好记的方法:

boolean offer(E e) 添加尾元素
boolean offerFirst(E e) 添加首元素
boolean offerLast(E e) 添加尾元素
E peek() 获取首元素
E peekFirst() 获取首元素
E peekLast() 获取尾元素
E poll() 返回并移除头元素
E pollFirst() 返回并移除头元素
E pollLast() 返回并移除尾元素

  1. Map接口
    Map接口的特点
    1. map集合的结构是:键值对、KEY与VALUE、Map.Entry<K,V>的映射关系
    2. map中key值不允许重复,如果重复,对应的value会被覆盖
    3. map中的映射关系是无序的
    4. map没有自己的迭代器,所以迭代时通常需要转成set集合来迭代

简单方法:

void clear() 清空集合
boolean equals(Object o) 判断集合对象与参数o是否相等
int hashCode() 返回本集合的哈希码值
boolean isEmpty() 判断集合是否为空
int size() 返回本集合中键值对的个数

map单个集合间的操作

boolean containsKey(Object key) 判断map中是否包含指定的key
boolean containsValue(Object value) 判断map中是否包含指定的value
V get(Object key) 根据指定的key返回对应的value,如果不存在,返回null
V remove(Object key) 删除本集合中参数key对应的键值对
V put(K key, V value) 向集合中添加映射关系(键值对)
void putAll(Map<> m) 向本集合中添加m集合的所有映射关系(键值对)

map的迭代

Collection values() 把本map中的Value值取出放入一个Collection中并返回这个Collection
Set keySet() 把本map中的Key值取出放入一个Set集合中并返回这个Set集合
Set<Map.Entry<K,V>> entrySet()
把本map中的每一对KV都看成是一个Entry,把所有的Entry取出放入一个Set集合中并返回这个Set集合

  1. Set接口
    Set接口的特点
    1. set集合没有重复的元素
    2. set集合的元素是无序的
    3. set集合可以存null值,并且null最多有一个
    4. 我们自定义对象如果想去重,需要在自定义类中添加重写的equals()与hashCode()
  2. 集合学习的方法
    学习父级的公共方法,学习子类的创建方式,学习各种集合的特点
    1. 关于List大多都是与下标有关的操作
    2. 关于Set通常都是去重的操作
    3. 关于map通常都是映射关系,也就是键值对
    4. API要常练习,方法互相之间没有任何关系,用哪个,查哪个

3. 进程与线程

  1. 程序:数据与指令的集合,而且程序是静态的
  2. 进程:运行中的程序,给程序加入了时间的概念,不同时间进程有不同的状态,进程是动态的,代表OS中正在运行的程序
    进程有独立性,动态性,并发性
  3. 并行:相对来说资源比较充足,多个CPU同时并发处理多个不同的进程
  4. 串行:相对来说资源不太充足,多个资源同时抢占公共资源,比如CPU
  5. 线程:线程是OS能够进行运算调度的最小单位
    一个进程可以拥有多个线程,当然,也可以只拥有一个线程,只有一个线程的进程称作单线程程序
    注意:每个线程也有自己独立的内存空间,当然也有一部分共享区域用来保存共享的数据
  6. 线程的几种状态以及线程状态之间的切换
    1)新建状态:创建线程对象,申请PCB,对应的是new线程对象
    2)就绪状态/可运行状态:万事俱备,只欠CPU,刚刚创建好的线程对象所有资源已经准备好,并且加入到了就绪队列之中
    唯有等待操作系统的调度,只要分配了CPU,也就是时间片,当前线程可立即执行,对应的是start()
    注意:调用start()并不会立即执行线程对象,这个是由OS的调度规则决定的。我们控制不了
    3)执行/运行状态:就绪队列中的线程对象被OS选中,分配了时间片,正在执行
    注意:只有就绪状态才能变成运行状态
    4)阻塞状态:线程在执行过程中遇到了问题,比如锁阻塞、休眠阻塞、等待阻塞…
    注意:我们的阻塞状态,等问题解决了以后/获取了临界资源【要抢占的公共资源】后
    是加入到就绪队列中的,转为就绪状态,而不是转为运行状态直接执行
    5)终止状态:线程成功执行完毕,释放资源,归还PCB
    6)线程的挂起:正在运行中的线程,由于CPU分配的时间片已经用完,所以需要冻结当前线程运行的状态与各项信息
    把它插入到就绪队列中,直到下次这个线程被调度执行时,重新恢复现场,继续执行
    在这里插入图片描述
  7. 多线程编程实现方案一:extends Thread继承方式
    1)自定义一个多线程类用来继承Thread类
    2)重写run()里的业务【这个业务是自定义的】
    3)创建线程对象【子类对象】
    4)通过刚刚创建好的自定义线程类对象调用start()
    注意1:不能调用run(),因为这样调用只会把run()看作一个普通的方法,并不会以多线程的方式启动程序
    而且调用start()时,底层JVM会自动调用run(),执行我们自定义的业务
    注意2:我们除了可以调用默认的父类无参构造以外,还可以调用Thread(String name),给自定义的线程对象起名字,相当于super(name);
  8. 多线程编程实现方案二:implements Runnable 实现方式
    1)自定义一个类实现接口Runnable
    2) 实现接口中唯一一个抽象方法run()
    3) 创建接口实现类的对象,这个对象是作为我们的目标业务对象【因为这个自定义类中包含了我们的业务】
    4)创建Thread类线程对象,调用的构造函数是Thread(Runnable target)
    5)通过创建好的Thread类线程对象调用start(),以多线程的方式启动同一个业务target
    注意1:由于Runnable是一个接口,无法创建对象,所以我们传入的目标业务类,也就是接口实现类的对象
    注意2:只有调用start()才能把线程对象加入到就绪队列中,以多线程的方式启动,但是:
    接口实现类与接口都没有这个start(),所以我们需要创建Thread类的对象来调用start(),并把接口实现类对象交给Thread(target);
    大家可以理解成“抱大腿”,创建的是Thread类的线程对象,我们只需要把业务告诉Thread类的对象就好啦
    使用方式二的优势:
    1)耦合性不强,没有继承,后续仍然可以继承别的类
    2)采用的是实现接口的方式,后续仍然可以实现别的接口
    3)可以给所有的线程对象统一业务,业务是保持一致的
    4)面向接口编程,有利于我们写出更加优雅的代码

猜你喜欢

转载自blog.csdn.net/weixin_43884234/article/details/122105717
今日推荐