我是靠谱客的博主 可耐耳机,最近开发中收集的这篇文章主要介绍javaseJava基础总结一、程序设计基础二、数组冒泡排序三、方法四、面向对象(基础)封装继承多态static关键字final关键字项目–零钱通本章作业项目–房屋出租系统面向对象(高级)抽象类接口枚举和注解异常机制集合基础集合进阶常用类线程IO流绘图技术IO流绘图技术,觉得挺不错的,现在分享给大家,希望可以做个参考。


typora-copy-images-to: …typora图片
typora-root-url: …typora-user-images

文章目录

    • typora-copy-images-to: ..typora图片 typora-root-url: ..typora-user-images
  • Java基础总结
  • 一、程序设计基础
    • **1标识符**
      • 1标识符释义
    • **2关键字**
    • **3****常量**
    • 4数据类型
    • **5变量**
    • **6类型转换**
    • **7运算符**
    • **8数据输入**
    • **9流程控制**
  • **二、数组**
    • **1数组**
      • ****2、数组初始化之动态初始化****
      • **3、数组元素访问**
      • ***4、java中内存分配****
    • **2、数组初始化之静态初始化**
    • **6、数组操作的两个常见小问题**
      • ****6.1索引越界:访问了数组中不存在的索引对应的元素,造成索引越界问题****
    • **7、数组常见操作**
      • ****7.1遍历****
      • ****7.2获取数组元素数量****
      • ****7.3获取最值****
  • 冒泡排序
    • 基本思想是
  • **三、方法**
    • **1方法的概述**
    • **2方法的定义和调用**
      • ****2.2方法调用****
    • **3带参数方法的定义和调用**
      • ****3.1带参数方法定义****
      • ****3.2带参数方法调用****
      • ****3.3形参和实参****
    • **4带返回值方法的定义和调用**
      • ****4.2****
    • **5方法的注意事项:**
      • ****1 方法不能嵌套定义****
      • **2.** ****方法的通用格式****
    • **6方法重载**
      • ****6.1方法重载概述****
    • **7方法的参数传递(基本类型)**
      • ****7.2方法参数传递(引用类型)****
    • **8Debug**
  • **四、面向对象(基础)**
    • **1类和对象**
      • ****1.1什么是对象****
      • ****1.2什么是面向对象****
      • ****1.3什么是类****
      • ****1.4什么是对象的属性****
      • ****1.5什么是对象的行为****
      • ****1.7类的定义****
      • ****1.8对象的使用****
    • **2什么是成员变量个局部变量**
      • ****3.2成员变量和局部变量区别****
    • **3private关键字**
    • **4this关键字**
    • **5封装**
    • **6String**
    • **7Strinbuilder概述**
    • **1**ArrayList****的构成
  • **封装**
      • 定义
      • get/set方法的作用
      • 注意点
      • 意义
      • 重点记住
  • 继承
      • 继承的本质
      • 注意点
      • super关键字
      • 注意点
      • super和this比较
      • 方法重写
        • 方法是静态的情况
        • 方法不是静态的情况
        • 重写的注意点
        • 重写本质
        • 为什么要重写
        • 快捷键
  • 多态
    • 基本介绍
    • 多态的具体体现
      • 方法的多态
      • 对象的多态(核心,困难,重点)
      • 多态注意事项和细节讨论
      • 多态的向下转型
      • java的动态绑定机制(非常非常非常重要)
    • 多态的应用
      • 多态数组
      • 多态参数
  • static关键字
      • 静态属性
      • 静态方法
      • 代码块
      • 匿名代码块、静态代码块、构造方法的执行顺序
      • 静态导入包
      • 用static修饰常量有什么好处?
  • final关键字
    • 1.修饰变量
    • 2.修饰方法
    • 3.修饰类
    • 4.深入分析final关键字
  • 项目--零钱通
    • 第一个版本
    • 第二版本oop
  • 本章作业
    • 作业一
    • 作业二
    • 作业三
    • 作业四
    • 作业六
    • 作业七
    • 作业八
    • 作业九
    • 作业十
    • 作业十一
    • 作业十二
    • 作业十三
    • 作业十四
    • 作业十五
    • 作业十六
  • 项目--房屋出租系统
    • 房子的类
    • 工具类
    • 实现类
    • 主菜单类
    • 服务类
  • 面向对象(高级)
    • 类变量和类方法
      • 快速入门
        • 类变量---提出的问题
        • 类变量内存布局
      • 系统学习
        • 什么是类变量
        • 如何定义类变量
        • 如何访问类变量
        • 类变量使用注意事项和细节
        • 类方法
          • 形式如下:
          • 类方法的调用
          • 小结
          • 类方法使用注意事项和细节讨论
          • 小结: 静态方法,只能访问静态的成员,非静态的方法,可以访问静态成员和非静态的成员(必须遵守访问权限)
      • 课堂练习
        • 题一
        • 题二
        • 题三
    • 理解main方法语法
    • 代码块
      • 代码块快速入门
        • 基本介绍
        • 基本语法
        • 代码块的好处
      • 代码块使用注意事项和细节讨论(重要)
        • 创建一个对象时,在一个类 调用顺序是:(重点,难点):
        • 陈士虎的理解:
      • 课堂练习
        • 题一
        • 题二
  • 抽象类
      • 为什么要用抽象类
      • 注意
      • 特点
      • 抽象类和普通类的区别
      • abstract 关键字,和哪些关键字不能共存。
  • 接口
      • 本质
      • 特点
      • 作用
  • 枚举和注解
      • 自定义类实现枚举
      • enum关键字实现枚举
      • JDK内置的基本注解类型
      • 元注解:对注解进行注解
  • 异常机制
      • 什么是异常
      • 异常体系结构
      • java异常处理机制
        • 抛出异常
        • 捕获异常
        • 异常处理的五个关键字
      • 处理异常
      • 自定义异常
      • 总结
  • 集合基础
        • 1.1ArrayList集合
        • 1.2ArrayList构造方法和添加方法
        • 1.3ArrayList集合常用方法
  • 集合进阶
        • Collection
          • 1. 集合类体系结构
          • 2. Collection集合概述和使用
          • 3. Collection集合常用方法
          • 4. Collection集合的遍历
          • 5集合的使用步骤
          • 案例:Collection集合存储学生对象并遍历
        • List
          • 1. List集合概述和特点
          • 2. List集合特有的方法
          • 案例:List集合存储学生对象并遍历
          • 3 . 并发修改异常
          • 4. ListIterator
          • 5。增强for循环
          • 6数据结构
            • 队列
            • 数组
            • 链表
        • Set
          • 1. Set集合概述和特点
          • 2. 哈希值
          • 3. HashSet集合概述和特点
          • 4. 哈希表
          • 案例
          • 5. LinkedHashSet集合概述和特点
          • 6. TreeSet
          • 7. 比较器排序Comparator的使用
          • 案例:成绩排序
          • 案例:不重复的随机数
        • 泛型
          • 1. 概述
          • 2. 泛型类
          • 3. 泛型方法
          • 4. 泛型接口
          • 5. 类型通配符
          • 6. 可变参数
        • Map
          • 1. Map集合概述和使用
          • 2. Map集合的基本功能
          • 3. map集合的获取功能
          • 4. map集合的遍历(方式一)
          • 5. map集合的遍历(方式二)
          • 案例(一)
          • 案例(二)
          • 案例(三)
          • 案例(四)
        • Collections
          • 1.collections类的概述
          • 案例(一)
          • 案例(二)
          • 案例(三)
  • 常用类
      • 内部类
        • 静态内部类
        • 匿名内部类
        • 局部内部类
        • 成员内部类
      • Object类
          • 定义
          • getClass()方法
          • hashCode()方法
          • toString()方法
          • equals()方法
          • finalize()方法
      • 包装类
          • 5.1 基本类型包装类概述
          • 5.2 Integer类的概述和使用
          • 5.3 int和String的相互转换
          • 案例:字符串数据排序
          • 5.4自动装箱和拆箱
      • Date类
        • 1 Date类概述和构造方法
        • 2 Date类的常用方法
  • 线程
      • 线程介绍
        • 程序
        • 进程
        • 线程
        • 单线程
        • 多线程
        • 并发
        • 并行
      • 线程使用(重要)
        • 1. 创建线程的两种方式
        • 2. 线程应用案例1,继承Thread类
        • 3. 线程应用案例2:-实现Runnable接口
        • 4. 线程使用应用案例-多线程执行
        • 5. 继承Tread vs 实现Runnable的区别
        • 6. 线程终止
      • 线程方法
        • 常用方法(一)
        • 注意细节
        • 常用方法(二)
        • 课堂练习
        • 用户线程和守护线程
      • 线程的声明周期[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c341F8AW-1640005796005)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211130150450135.png)]
      • Synchronized
        • 线程同步机制
        • 同步具体方法
        • 注意事项
        • 线程的死锁
        • 释放锁
      • 本章作业
        • 1.编程题
        • 2.编程题
  • IO流
    • 文件
      • 什么是文件
      • 获取文件的相关信息
      • 目录的操作和文件删除
    • InputStream:字节输出流
  • 绘图技术
        • 画圆
        • 绘制图片的方法[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hsif68Jl-1640005796010)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211201134550972.png)]
        • java事件处理机制
        • 本章作业[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PwRrbfT0-1640005796012)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211201203001788.png)]
        • 线程的死锁
        • 释放锁
      • 本章作业
        • 1.编程题
        • 2.编程题
  • IO流
    • 文件
      • 什么是文件
      • 获取文件的相关信息
      • 目录的操作和文件删除
    • InputStream:字节输出流
  • 绘图技术
        • 画圆
        • 绘制图片的方法[外链图片转存中...(img-Hsif68Jl-1640005796010)]
        • java事件处理机制
        • 本章作业[外链图片转存中...(img-PwRrbfT0-1640005796012)]

Java基础总结

一、程序设计基础

1标识符

1标识符释义

标识符就是用于给程序中的变量、类、方法命名的符号。标识符可以由编程人员自由定义,但需要遵循一定的命名规则。

1.2标识符命名规则

可以由字母、数字、下划线(_)、美元符号($)组成。

必须以字母、下划线或美元符号开头,不能以数字开头。

不能指定为关键字。

长度没有限制,但定义时一般以见名知义为原则。

使用驼峰命名,类名首名字均大写,常量全部字母大写;方法名、属性名 的单词首字母小写中间的单词首字母大写。

包名aaabbbccc

类名、接口名:XxxYyyZzz

变量名、方法名:xxxYyyZzz

常量名:ZYZZ

2关键字

**2.*1释义 Java语言中用以特殊用途而预占用的单词或标识符称之为关键字,Java中的关键字均为小写。*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y4X3NjdW-1640005795952)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps7742.tmp.jpg)]

3****常量

常量:在程序运行过程中,其值不可以改变的量。

常量分类:

1字符串常量 用双引号括起来的类容 “Helloworld”“黑马程序员”

2整数常量 不带小数的数字 3333,-2

3小数常量 带小数的数字 13.23,

4字符常量 用单引号括起来的类容 ‘A’,’0’ ‘我’

5布尔常量 布尔值 ,表示真假 只有两个之:true,false

6空常量 一个特殊的值,空值 值士:null

只有空常量不能直接输出的,其他都可以直接输出。

4数据类型

1B(字节)=8bit

1KB=1024B

1MB=1024KB

1GB=1024MB

1TB=1024GB

整数默认int

浮点数默认double

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qjCgM1DO-1640005795953)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps7743.tmp.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZNecyOhg-1640005795954)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps7744.tmp.jpg)]

5变量

1.1变量:在程序运行过程中,其值可以发生改变的量。

从本质上讲,变量是内存中一小块区域。

1.2变量定义

格式:数据类型 变量名=变量值;

​ Int a=10;

1.3变量使用的注意事项

1 变量名不能重复

2 变量未赋值不能使用

3 定义long类型的时候防止整数过大在数值之后加L

Long l=100000000L;

4 float类型防止不兼容加个F

Float f=13.14F;

6类型转换

1自动类型转换

把一个表示数据范围小的数值或者变量复制给另一个表示数据范围大的变量

Byte、short、char–>int–>long–>float–>double

Double d=10;//10.0;

Byte b=10;可以

Short s=b;可以

Int i=b;可以

char c=b;不可以

2强制类型转换(数据会丢失)

把一个表示数据范围大的数值或者变量赋值给另一个表示范围小的变量

格式:目标数据类型 变量名=(目标数据类型)值或者变量

Int k=(int)88.88888; // 88

向上转型都是自动的。向下转型需要强制转型

7运算符

运算符:对常量或者变量进行操作的符号。

表达式:用运算符把常量或者变量连接起来符合Java语法的式子就可以称为表达式。

​ 不同运算符连接的表达式体现的是不同类型的表达式。

​ +: 是运算符,并且是算术运算符

​ a+b:是表达式,由于+是算术运算符,所以这个表达式叫算术表达式。

符号 作用 说明

​ + 加 参看小学一年级

​ - 减 参看小学一年级

​ * 乘 参看小学二年级,与”x“相同

​ / 除 参看小学二年级,与”除法“相同

​ % 取余 获取的是两个数据做除法的余数

整数操作只能得到整数,要想得到小数,必须有浮点数参与运算。

例子

int i=10;

char c=’A’;//65

C=’0’//48

System.out.println(i+c);//75

A-Z是连续的 A是65

a-z是连续的 a是97

0-9是连续的 0是48

1算数表达式中包含多个基本数据类型的值的时候,整个算术表达式的类型会自动提升。

2整个表达式的类型自动提升到表达式中最高等级操作数同样的类型。

字符串的“+”的操作

System.out.println(“it”+”heima”)//itheima

System.out.println(“itheima”+666)//itheima666

System.out.println(666+”ithema”)//666ithema

System.out.println(“黑马”+6+6)//黑马66

System.out.println(1+99+“黑马”)100黑马

1当“+”操作中出现字符串是,这个“+”是字符串连接符,而不是算数运算。

2在“+”操作中,如果出现了字符串,就是连接运算符,否则就是算数运算。当连续进行“+“操作时,从左到右逐个执行。

例子

Short s=20;

S+=20;

S=s+20;//错的,类型的自动提升。对的是这样的 s=(short)(s+20)

扩展的赋值运算符隐含了强制类型转换,

​ 自增自减运算符

i++。++i

例子:

Int i=10;

Int j=i++;

System.out.println(i)//11

System.out.println(j)//10

Int i=10;

Int j=++i;

System.out.println(i)//11

System.out.println(j)//11

注意事项:

1 ++和–既可以放在变量的后面,也可以放在变量的前面。

2 单独使用的时候,++和–无论放在变量的前边还是后边,结果都一样的。

3 参与操作的时候,如果放在变量的后边,先拿变量参与操作,后拿变量做++或–.

参与操作的时候,如果放在变量的前面,先拿变量做++或这–,后拿变量参与操作。

关系运算符

==、!=、>、>=、<、<=

注意事项:

关系运算符的结果都是Boolean类型,要么是true,要么是false。千万不要吧”==“误写成”=“

8数据输入

1 import java.util.Scanner;

2 Scanner sc=new Scanner(System.in);

3 int height =sc.nextlnt();

9流程控制

1 顺序结构

2 分支结构

3 循环结构

Switch中如果所有case的值和表达式的值都不匹配,就会执行default里面的语句体,然后程序结束掉。

case穿透

例子:

Switch(month)

{

case 1:

case 2:

Case 3:

System.out.println(“春季”);

break;

Case 4:

Case 5:

Case 6:

System.out.println(“冬季”);

break;

}

For循环语句

二、数组

1数组

****1 数组概述:****一次性声明大量用于存储数据的变量;

要存储的数据通常都是同类型数据;

什么是数组:

数组是一种用于存储多个相同类型数据的存储模型;

数组的定义格式(推荐用格式一)

格式一: 数据类型[] 变量名

​ Int[] arr

定义了一个int类型的数组,数组名是arr。

格式二: 数据类型 变量名[]

​ Int arr[]

定义了一个int类型的数组,数组名是arr。

*2、数组初始化之动态初始化*

2.1数组初始化概述

​ Java中的数组必须先初始化,然后才能使用

​ 所谓初始化:就是为数组中的数组元素分配内存空间,并为每个数组元素赋值;

2.2动态初始化

初始化时只指定数组长度,由系统为数组分配初始值;

格式: 数据类型[] 变量名=new 数据类型[数组长度]

​ Int[] arr=new int[3];

*3、数组元素访问*

索引时数组中数据的编号方式

作用:索引用于访问数组中的数据使用,数组名[索引]等同于变量名,是一种特殊的变量名

特征:索引从0开始的

特征:索引时连续的

特征:索引逐一增加,每次加一;

4、java中内存分配*

Java程序在运行时,需要在内存中分配空间,为了提高运算效率,就对空间进行了不同区域的划分。

因为每一片区域都有特定的处理方式和内存管理方式。

数组在初始化时,会为存储空间添加默认值

整数: 0

浮点数 0.0

布尔值 false

字符: 空字符

引用数据类型:null

Int[] arr=new int[3]

栈内存:存储局部变量

定义方法中的变量:例如arr

使用完毕,立即消失

堆内存:存储new出来的类容(实体,对象)

每一个new出来的东西都有一个地址值使用完毕,会在垃圾回收站空闲时被回收

数组内存图(多个数组指向相同)

例子:

img

2、数组初始化之静态初始化

****4.1静态初始化:****(推荐简化格式)

初始化时指定每个元素的初始值,由系统决定数组长度

格式: 数据类型[] 变量名=new 数据类型[]{数据一,数据二,数据三}

​ Int[] arr =new int[]{1,2,3}

简化格式 数据类型[] 变 量名={数据一,数据二,数据三}

Int[] arr ={1,2,3}

6、数组操作的两个常见小问题

*6.1索引越界:访问了数组中不存在的索引对应的元素,造成索引越界问题*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ygtW18WA-1640005795956)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps7755.tmp.jpg)]

空指针异常:访问的数组已经不在指向堆内存的数据,造成空指针异常;

Null:空值,引用数据类型的默认值,表示不指向任何有效对象

7、数组常见操作

*7.1遍历*

例子

Int[] arr={1,2,3}

For(int i=0;i<3;i++)

{System.out.println(arr[i])}

*7.2获取数组元素数量*

格式: 数组名.length

范例:arr.length

*7.3获取最值*

Int[] arr={1,2,3,4,5};

Int max=a[0];

For(int i=1;i<arr.length;i++)

{

If(max<a[i])

{

Max=a[i]

}

}

System.out.println(max);

冒泡排序

基本思想是

通过对待排序序列从后向前(从下标较大的元素开始),依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后部,就像水底的气泡一样逐渐向上冒

三、方法

1方法的概述

1.1什么是方法

方法时将具有独立功能的代码块组织成为一个整体,使其具有特殊功能的代码集

注意:

方法必须要先创建才可以使用,该过程称为方法定义

方法创建后并不是直接运行的,需要手动使用后才可以执行,该过程称为方法调用

2方法的定义和调用

2.1

格式:public static void 方法名()

{方法体

}

*2.2方法调用*

格式:方法名();

例子:

Public static max()

{

If(a>b)

{System.out.println(a);

}

Else{System.out.println(b)

}

}

3带参数方法的定义和调用

*3.1带参数方法定义*

格式:public static void 方法名(){…}

格式(单个参数):public static void 方法名(数据类型 变量名){…}

格式(多个参数):public static void 方法名(数据类型 变量名,数据类型 变量名…){。。。。。}

注意:

方法定义时,多个参数之间使用逗号(,)隔开

*3.2带参数方法调用*

格式:方法名(参数)

注意:方法调用时,参数的数量与类型必须与方法定义中的设置相匹配,否则程序将报错。

*3.3形参和实参*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8gvpVRj5-1640005795957)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps7756.tmp.jpg)]

4带返回值方法的定义和调用

格式:public static 数据类型 方法名(参数)

{

Return 数据;

}

注意:

方法定义时return后面的返回值与方法定义上的数据类型要匹配,否则程序将报错

*4.2*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1q8wJzSl-1640005795958)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps7757.tmp.jpg)]

5方法的注意事项:

*1 方法不能嵌套定义*

****2 void表示无返回值****,可以省略return,也可以单独的书写,return,后面不加数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nXS5uGI4-1640005795959)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps7758.tmp.jpg)]

2. *方法的通用格式*

格式:public static 返回值类型 方法名(参数)

{

方法体;

Return 数据;

}

Public static 修饰符,目前先记住这个格式

返回值类型 方法操作完毕之后的数据的数据类型

​ 如果方法操作完毕没有数据返回,这里写void,而且方法体

不写return

方法名 调用方法时侯使用的标识

参数 由数据类型和变量名组成,多个参数之间用逗号隔开

定义方法时,要做到两个明确

明确返回值类型:主要明确方法操作完毕之后是否有数据返回,如果没有,写void,如果有,写对应的数据类型

明确参数:主要是明确参数的类型和数量

调用方法时;

Void类型的方法,直接调用即可,

非void类型的方法,推荐用变量接收调用

6方法重载

*6.1方法重载概述*

方法重载指同一个类中定义的多个方法之间的关系,满足下列条件的多个方法相互构成重载

1 多个方法在同一个类中

2 多个方法具有相同的方法名

3 多个方法的参数不相同,类型不同或者数量不同

****6.2方法重载的特点****(只看方法名和参数和类型无关)

1 重载仅对应方法的定义,与方法的调用无关,调用方式参照标准格式

2 重载仅针对同一个类中方法的名称与参数进行识别,与返回值无关,换句话说不能通过返回值来判定两个方法是否相互构成重载。。

7方法的参数传递(基本类型)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AmoQEwdU-1640005795960)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps7769.tmp.jpg)]

*7.2方法参数传递(引用类型)*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DVW3DEOF-1640005795961)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps776A.tmp.jpg)]

案例:

设计一个方法用于数组遍历,要求遍历的结果是在一行的,

package test;

public class xjao

public static void main(String[] args) {

int[] arr={1,2,3,4,5};ye(arr); }public static void ye(int[] arr) {for(int i=0;i<arr.length;i++)

{

System.*out*.print(arr[i]+" ");

}

}

案例:设计一个方法用于获取数组中元素的最大值,调用方法并输出结果

public static void main(String[] args) {

int[] a={1,21,3,4,5};

max(a);

}

public static void max(int[] a)

{ int b=a[0];

for(int i=0;i<a.length;i++)

​ {

if(b<a[i])

​ {b=a[i];

​ }

}

System.*out*.print(b);

8Debug

}

1.1Debug概述

Debug:是供程序员使用的程序调试工具,它可以用于查看程序的执行流程,也可以用于追踪程序执行过程来调试程序。

1.2debug操作流程

1 如何加断点

2 如何运行加了断点的程序

3 看哪里

4 点哪里

5 如何删除断点

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lxDT2gjm-1640005795962)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps776B.tmp.jpg)]

public static void main(String[] args)

{

int[] a=new int[20];

​ a[0]=1;

​ a[1]=1;

for(int i=2;i<a.length;i++)

​ a[i]=a[i-1]+a[i-2];

System.*out*.print(a[19]);

​ }

}}

四、面向对象(基础)

1类和对象

*1.1什么是对象*

万物皆对象,客观存在的事物皆为对象

*1.2什么是面向对象*

*1.3什么是类*

类是对现实生活中一类具有共同属性和行为的事物的抽象。

类的特点:

1 类是对象的数据类型

2 类是具有相同属性和行为的一组对象的集合

*1.4什么是对象的属性*

属性:对象具有的各种特征,每个对象的每个属性都拥有特定的值。

*1.5什么是对象的行为*

行为:对象能够干什么

****1.6类和对象的关系****(类是对象的抽象,对象是类的实体)

类:类是对现实生活中一类具有共同属性和行为的事物的抽象

对象:是能够看得到摸得着的真实存在的实体。

*1.7类的定义*

类的重要性:是java程序的基本组成单位;

类的组成:属性和行为

1 属性:在类中通过成员变量来体现(类中方法外的变量)

2 行为:在类中通过成员方法来体现(和前面的方法相比去掉static关键字)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e5iNxOiq-1640005795963)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps776C.tmp.jpg)]

*1.8对象的使用*

创建对象

格式:类名 对象名=new 类名();

Phone p=new phone();

适用对象

1:使用成员变量

格式:对象名.变量名

2:使用成员方法

格式:对象名.方法名();

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fImqcS4t-1640005795963)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps776D.tmp.jpg)]

其中s1是new一个的,所以s1是一个地址值;

2什么是成员变量个局部变量

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yKdMrIJN-1640005795964)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps776E.tmp.jpg)]

*3.2成员变量和局部变量区别*

区别 成员变量 局部变量

类中位置不同 类中方法外 方法内或者方法声明上

内存中位置不同 堆内存 栈内存

生命周期不同 随着对象的存在而存在,随着 随着方法的调用而存在,

对象的消失而消失 随着方法的调用完毕二消失

初始化值不同 有默认的初始化值 没有默认的初始化值,必须先定义赋值,才能用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JtR4SYp3-1640005795964)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps777F.tmp.jpg)]

4. *封装*

*1*

*11*

3private关键字

1 是一种权限修饰符

2 可以修饰成员(成员变量和成员方法)

3 作用是保护成员不被别的类使用,被private修饰的成员只在本类中才能采访

针对private修饰的成员变量,如果需要被其他类使用,提供相应的操作

1 提供“get变量名()”方法,用于获取成员变量的值,方法用public修饰

2 提供“set变量名(参数)”方法,用于设置成员变量的值,方法用public修饰

1和2的变量名的首字母要大写

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VGe6SgnE-1640005795965)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps7780.tmp.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DBf8TZKX-1640005795966)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps7781.tmp.jpg)]

就可以在set方法里面设置范围

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1jXd7e31-1640005795966)(file:///C:UsersADMINI1AppDataLocalTempksohtmlwps7782.tmp.jpg)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FTukyp0L-1640005795967)(file:///C:UsersADMINI1AppDataLocalTempksohtmlwps7783.tmp.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AXbvmf5c-1640005795967)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps7784.tmp.jpg)]

这里的this.name是成员变量,另一个name是局部变量

4this关键字

1 this修饰的变量用于指代成员变量

1.1方法的形参如果与成员变量同名,不带this修饰的变量指的是形参,而不是成员变量。

1.2方法的形参没有与成员变量同名,不带this修饰的变量指的是成员变量。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iuACO4nr-1640005795967)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps7794.tmp.jpg)]

2 什么时候使用this呢,

只有当局部变量和成员变量同名时。

3 this:代表所在类的对象引用

记住:方法被那个对象调用,this就代表那个对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xAwI577I-1640005795968)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps7795.tmp.jpg)]

5封装

1 封装概述

是面向对象三大特征之一(封装、继承、多态)

是面向对象编程语言对客观世界的模拟,客观世界里成员变量都是隐藏子啊对象内部的,外界是无法直接操作的

2. 封装原则

将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问成员变量private,提供对应的getXxx()/setXxx()方法。

3. 封装好处

通过方法来控制成员变量的操作,提高了代码的安全性。

把代码用方法进行封装,提高了代码的复用性。

5构造方法

5.1构造方法概述

构造方法是一种特殊的方法

作用:创建对象

格式:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B44fCFX6-1640005795968)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps7796.tmp.jpg)]

5.2构造方法的注意事项

1 构造方法的创建

如果没有定义构造函数,系统将给出一个默认的无参数构造方法。

如果定义了构造方法,系统将不再提供默认的构造方法。

2 构造方法的重载

如果自定义了带参构造方法,还要使用无参数构造方法,就必须再写一个无参数构造方法

3 推荐的使用方法

无论是否使用,都手工书写无参数构造方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tYVy8wep-1640005795969)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps7797.tmp.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FQpz6Sip-1640005795970)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps7798.tmp.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Xwj186ip-1640005795970)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps7799.tmp.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D057as7S-1640005795971)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps779A.tmp.jpg)]

6String

String类在java.lang包下,所以使用的时候不需要导包

String类代表字符串,java程序中的所有字符串文字(例如“abc”)都被实现为此类的实例也就是说,java程序中所有的双引号字符串,都是String类的对象,

字符串的特点

1字符串不可变,他们的值在创建后不能被改变。

2 虽然String的值是不可改变的,但是它们可以被共享

3 字符串效果上相当于字符数组(char[]),但是底层原理是字节数组(byte[])

2.4字符串的比较

使用==做比较

1 基本类型:比较的是数据值是否相同

2 引用类型:比较的是地址值是否相同

字符串是对象,它比较内容是否相同,是通过一个方法来实现,这个方法叫:equals()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sV50esxh-1640005795971)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps77AB.tmp.jpg)]

案例:用户登录

public static void main(String[] args) {

​ String user=“chenshihu”;

​ String password=“taoyu”;

​ for(int n=0;n<3;n++){

​ Scanner sc=new Scanner(System.in);

​ System.out.println(“请输入用户名:”);

​ String a=sc.nextLine();

​ System.out.println(“请输入密码:”);

​ String b=sc.nextLine();

​ if(a.equals(user)&&b.equals(password)){

​ System.out.println(“用户名和密码正确”);

​ break;}

​ Else

​ {System.out.println(“密码错误,还有”+(2-n)+“次机会”);}

​ }

​ }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yn6tAT5h-1640005795972)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps77AC.tmp.jpg)]

案例:遍历数组

package test;

import java.util.Scanner;

public class Student

{

​ public static void main(String[] args) {

​ Scanner a=new Scanner(System.in);

​ String b=a.nextLine();

​ for(int n=0;n<b.length();n++)

​ {

​ System.out.println(b.charAt(n));

​ }

​ }

}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pOETI2c2-1640005795972)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps77AD.tmp.jpg)]

package test;

import java.util.Scanner;

public class DebugTest {

public static void main(String[] args) {

Scanner sc=new Scanner(System.*in*);

String link=sc.nextLine();

int a=0;

int b=0;

int c=0;

for(int n=0;n<link.length();n++)

​ {

char ch=link.charAt(n);

if(ch<=‘Z’&&ch>=‘A’)

​ { a++;}

else if(ch<=‘z’&&ch>=‘a’)

​ {b++;}

else if(ch<=‘9’&&ch>=‘0’)

​ {c++;}

​ }

System.*out*.println(“a=”+a+",b="+b+",c="+c);

​ }

}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4B3uYTQ0-1640005795973)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps77AE.tmp.jpg)]

字符反转的有点没看懂。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0hYAB2Ho-1640005795973)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps77AF.tmp.jpg)]

package test;

import java.util.Scanner;

public class DebugTest {

public static void main(String[] args) {

Scanner sc=new Scanner(System.*in*);

String link=sc.nextLine();

String b =text(link);

System.*out*.println(b);

​ }

public static String text(String b){

String x="";

for(int n=b.length()-1;n>=0;n–)

​ {

​ x+=b.charAt(n);

}

return x;

​ }

}

7Strinbuilder概述

如果对字符串进行拼接操作,每次拼接,都会构建一个新的String对象,既耗时,又浪费内存空间,而这种操作还不可避免,那么有没有一种比较好的方式可以解决这个问题呢?答案是肯定的,我们可以通过Java提供的StringBuilder类就来解决这个问题。

StringBuilder是一个可变的字符串类,我们可以把它看成是一个容器这里的可变指的是StringBuilde对象中的内容是可变的。

String和StringBuilde的区别

1 String:内容是不可变的

2 StringBuilde:内容是可变的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cP98bZFL-1640005795974)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps77C0.tmp.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qeI3MSNR-1640005795975)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps77C1.tmp.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V7sbamJW-1640005795975)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps77C2.tmp.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pOd5dkOj-1640005795976)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps77C3.tmp.jpg)]

3.4StringBuilder和String相互转换

1.StringBuilder转换为String

Public String to String :通过toSting()就可以实现把String Builder转换为String;

2String转换为StringBuilder

Public StringBuilder(String s):通过构造方法就可以实现把String 转换为StringBuilder

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E7hCi5lu-1640005795977)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps77C4.tmp.jpg)]

![img](file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps77C5.tmp.jp、集合基础**

集合类的特点:提供一种存储空间可变的存储模型,存储的数据容量可以发生改变

集合类有很多,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CD1WgpEm-1640005795977)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps77D5.tmp.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wixVzMnf-1640005795979)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps77D6.tmp.jpg)]

package test;

import java.util.ArrayList;

import java.util.Scanner;

public class DebugTest {

public static void main(String[] args) {

ArrayList a=new ArrayList<>();

​ a.add(“chenshihu”);

​ a.add(“taoyu”);

System.*out*.print(a);

​ }

}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ugln7R2b-1640005795979)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps77D7.tmp.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F1XKsv22-1640005795980)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps77D8.tmp.jpg)]

1ArrayList****的构成

1 ArrayList a=new ArrayList<>();

2 ArrayList a=new ArrayList();

都可以

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WPRaSABm-1640005795981)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps77D9.tmp.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oR9B5b8i-1640005795981)(file:///C:UsersADMINI~1AppDataLocalTempksohtmlwps77DA.tmp.jpg)] 继承

​ 1.1继承概述

继承是面向对象三大特征之一。可以使得子类具有父类的属性和方法,还可以在子类中重新定义,追加属性和方法

继承的格式:

格式:public class 子类名 extends 父类名()

范例:public class zi extends fu()

Fu:是父类,也被称为基类、超类

Zi:是子类,也被称为派生类s

封装

定义

封装(数据的隐藏):通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏。

get/set方法的作用

  • 提供一些可以操作这些属性的方法

  • 提供一些public的get/set方法

  • get获得这个数据

public String getName()
{
return this.name;
}
  • set给这个数据设置值
public void setName(String name)
{
this.name=name;
}
  • set还可以对这个属性值进行限制
public void setAge(String age)
{
if(age>120||age<0)
{
this.age=age;
}
else
{
this.age=3;
}
}

注意点

  • 属性私有

  • get/set方法后面那个单词首字母大写

  • 自动生成快捷键:alt+insert

意义

  • 提高程序的安全性,保护数据
  • 隐藏代码的是实现细节
  • 统一接口
  • 系统可维护增加了

重点记住

  • 属性私有

  • get/set

继承

继承的本质

是对某一批类的抽象,从而实现对现实世界更好的建模。

注意点

  • extends的意思是“扩展”,子类是父类的扩展。

  • java中类只有单继承,没有多继承!

  • 继承是类和类之间的一种关系。除此之外,类和类之间还有依赖、组合、聚合等。

  • 继承关系的两个类,一个为子类(派生类),一个为父类(基类),使用关键字extends来表示。

  • public class Student extends Person{
    }
    
  • 子类继承了父类,就会拥有父类的全部方法!

  • 在Java中,所有的类,都默认直接或者间接继承object类

  • 父类私有的东西无法被继承

  • 一个类被final修饰的类没有子类,没有办法被其他类继承

super关键字

父类
public class Person
{
String name="父类"
public Person()
{
System.out.println("调用了父类的无参构造器")
}
/* 2
public void print()
{
System.out.println("父类的print")
}
*/
}
子类
public class Student extends Person
{
private String name="子类"
String name="父类"
public Person()
{
//隐藏了super();
System.out.println("调用了子类的无参构造器")
}
/* 1
public void test( String name){
System.out.println(name);
//测试类
System.out.println(this.name);
//子类
System.out.println(super.name);
//父类
}
*/
/* 2
public void print()
{
System.out.println(“子类的print”)
}
public void test1()
{
print();
//子类的print()
this.print();
//子类的print()
super.print();
//父类的print()
}
*/
}
测试类
public class Test
{
public static void main(String[] args)
{
// 1 Student student=new Student();
// 1 student.test(测试类);
// 2 student.test1();
// 3 Student student=new Student();
//调用了父类的无参构造器
//调用了子类的无参构造器
}
}
  • 构造器this()和super() 如果写的话必须放在第一行。一个构造器中只有其中一个;

  • 如果父类中的构造器是有参的,没有无参的,那在子类的的无参构造器可以默认且必须写出来,还会报错;

​ 解决办法:在子类的无参构造器中添加super(参数)就可以了

注意点

  1. super调用父类的构造方法,必须在构造方法的第一个
  2. super 必须只能出现在子类的方法或者构造方法中;
  3. super和this不能同时调用构造方法;

super和this比较

  • 代表的对象不同:

​ this:本身调用者这个对象

​ super:代表父类对象的应用

  • 构造方法

    this:本类的构造;

    super:父类的构造

  • 前提

    this:没有继承也可以使用

    super:只能在继承条件下才可以使用

方法重写

方法是静态的情况

子类
public class A extends B
{
public static void test()
{
System.out.println("A");
}
}
父类
public class B{
public static void test()
{
System.out.println("B");
}
}
测试类
public class Test
{
public static void main(String[] args)
{
//方法的调用只和左边,定义的数据类型有关
A a=new A();
a.test();
//A
//父类的引用指向了子类
B b=new A();
b.test();
//B
}
}

方法不是静态的情况

子类
public class A extends B
{
public void test()
{
System.out.println("A");
}
父类
public class B{
public
void test()
{
System.out.println("B");
}
}
测试类
public class Test
{
public static void main(String[] args)
{
A a=new A();
a.test();
//A
//子类重写了父类的方法 
B b=new A();
b.test();
//A
}
}

重写的注意点

  • 重写之和非静态有关
  • 方法只能是public才可以重写
  • 需要有继承关系,子类重写父类的方法!
  • 重写都是方法的重写,和属性无关。
  • 参数列表必须相同
  • 子类重写父类的方法的修饰符范围可以扩大 public>protected>Default>private

重写本质

子类的方法和父类的所有东西都一致,就方法体不同

对比抛出的异常范围,可以被缩小,但不可以扩大

为什么要重写

父类的功能,子类不一定需要,或者不一定满足

快捷键

alt+insert: override

多态

基本介绍

方法或对象具有多种形态,是面向对象的第三大特征,多态是建立在封装和继承基础之上的

多态的具体体现

方法的多态

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KmYUTNbS-1640005795982)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211210093244940.png)]

对象的多态(核心,困难,重点)

一定要记住这几句话

  1. 一个对象的编译类型和运行类型可以不一致
  2. 编译类型在定义对象时,就确定了,不能改变
  3. 运行类型是可以变化的
  4. 编译类型看定义时=号的左边,运行类型看=号的右边

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nmaTeifF-1640005795983)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211210095508982.png)]

package D03;
public class Animal {
public void cry()
{
System.out.println("Animal在哭");
}
}
class Dog extends Animal
{
@Override
public void cry() {
System.out.println("Dog在哭");
}
}
class Cat extends Animal
{
@Override
public void cry() {
System.out.println("Cat在哭");
}
}
class Test
{
//体验多态特点
public static void main(String[] args)
{
//a编译类型就是Animal ,运行类型Animal ;
Animal a=new Animal();
a.cry();
//a编译类型就是Animal ,运行类型Cat ;
a=new Cat();
a.cry();
//a编译类型就是Animal ,运行类型Dog;
a=new Dog();
a.cry();
}
}
/*Animal在哭
Cat在哭
Dog在哭
*/

多态注意事项和细节讨论

  • 多态的前提是:两个对象(类)存在继承关系
  • 多态的向上转型
  1. 本质:父类的引用指向了子类的对象
  2. 语法:父类类型 引用名=new 子类类型();
  3. 特点:
    • 编译类型看左边,运行类型看右边
    • 可以调用父类中的所有成员(x需遵守访问权限)
    • 不能调用子类中特有成员
    • 最终运行效果看子类的具体实现
package D03;
public class Animal {
String name="动物";
int age=10;
public void sleep()
{
System.out.println("睡");
}
public void run()
{
System.out.println("跑");
}
public void eat()
{
System.out.println("吃");
}
public void show()
{
System.out.println("你好");
}
}
class Cat extends Animal
{
public void eat()
{
System.out.println("猫吃鱼");
}
public void catchFish()
{
System.out.println("抓鱼");
}
}
class Test
{
public static void main(String[] args) {
//1. 本质:父类的引用指向了子类的对象
//2. 语法:父类类型
引用名=new
子类类型();
Animal a=new Cat();
//Object obj=new Cat(); 也可以;
//- 可以调用父类中的所有成员(x需遵守访问权限)
//- 不能调用子类中特有的成员
//因为在编译阶段,能调用那些成员(属性和方法),由编译类型决定的
//a.catchFish();
错了
//最终运行效果看子类(运行类型)的具体实现,即调用方法时,按照从子类(运行类型)开始查找方法
//然后调用后面的
//先看运行类型里面找,然后在去编译类型里面找
a.eat();
a.sleep();
a.show();
a.run();
}
}
/*
猫吃鱼
睡
你好
跑
*/

多态的向下转型

  1. 语法:子类类型 引用 名=(子类类型)父类引用
  2. 只能强转父类的引用,不能强转父类的对象
  3. 要求父类的引用必须指向的是当前目标类型的对象
  4. 可以调用子类类型中所有的成员
package D03;
public class Animal {
String name="动物";
int age=10;
public void sleep()
{
System.out.println("睡");
}
public void run()
{
System.out.println("跑");
}
public void eat()
{
System.out.println("吃");
}
public void show()
{
System.out.println("你好");
}
}
class Dog extends Animal
{
public void eat()
{
System.out.println("猫吃鱼");
}
public void catc()
{
System.out.println("吃pp");
}
}
class Cat extends Animal
{
public void eat()
{
System.out.println("猫吃鱼");
}
public void catchFish()
{
System.out.println("抓鱼");
}
}
class Test
{
public static void main(String[] args) {
//1. 本质:父类的引用指向了子类的对象
//2. 语法:父类类型
引用名=new
子类类型();
Animal a=new Cat();
//Object obj=new Cat(); 也可以;
//- 可以调用父类中的所有成员(x需遵守访问权限)
//- 不能调用子类中特有的成员
//因为在编译阶段,能调用那些成员(属性和方法),由编译类型决定的
//a.catchFish();
错了
//最终运行效果看子类(运行类型)的具体实现,即调用方法时,按照从子类(运行类型)开始查找方法
//然后调用后面的
//先看运行类型里面找,然后在去编译类型里面找
a.eat();
a.sleep();
a.show();
a.run();
//我们希望可以调用Cat的catchFish方法
//多态的向下转型
//语法:子类类型
引用名=(子类类型)父类引用
//b的编译类型是Cat,运行类型也是Cat
Animal d=new Dog();
//要求父类的引用必须指向的是当前目标类型的对象
//向下转型只能转成他的的运行类型
//披在羊皮的狼,只能转型成狼
Cat b=(Cat)a;
b.catchFish();
Dog c=(Dog)d;
c.catc();
}
}
/*
猫吃鱼
睡
你好
跑
抓鱼
吃pp
*/
  • 属性没有重写之说,属性的值看编译类型,
  • instanceOf比较操作符,用于判断对象的运行类型是否为xx类型或xx类型的子类型
package D03;
public class D03
{
public static void main(String[] args)
{
//属性没有重写之说,属性的值看编译类型,
Base a=new Sub();//向上转型
System.out.println(a.count);//看编译类型 10
}
}
class Base
{
int count=10;
}
class Sub extends Base
{
int count=20;
}
package D03;
public class D04
{
public static void main(String[] args) {
BB bb=new BB();
System.out.println(bb instanceof BB);
System.out.println(bb instanceof AA);
//aa编译类型是AA,运行类型是BB
AA aa=new BB();
System.out.println(aa instanceof BB);
System.out.println(aa instanceof AA);
Object c=new Object();
System.out.println(c instanceof AA);
String str ="hello";
System.out.println(str instanceof Object);
}
}
class AA {}
class BB extends AA{}
/*
true
true
true
true
false
true
*/

java的动态绑定机制(非常非常非常重要)

  1. 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
  2. 当调用对象属性时,没有动态绑定,哪里声明,哪里使用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0UJfvhjW-1640005795983)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211210192607159.png)]

package D03;
public class D05 {
public static void main(String[] args)
{
A a=new B();
System.out.println(a.sum());
System.out.println(a.sum1());
}
}
class A
{
public int i=10;
//动态绑定机制:
public int sum()
{
return getI()+10;
}
public int sum1()
{
return i+10;
}
public int getI()
{
return i;
}
}
class B extends A
{
public int i=20;
//
public int sum()
//
{
//
return getI()+20;
//
}
//
public int sum1()
//
{
//
return i+10;
//
}
public int getI()
{
return i;
}
}
30
20

多态的应用

多态数组

:数组的定义类型为父类类型,里面保存的实际元素类型为子类类型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GYqGQ9BM-1640005795984)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211212103956672.png)]

package D04;
public class Person {//父类
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String say()//返回名字和年龄
{
return name+" "+age;
}
}
package D04;
public class Student extends Person{
private double score;
public Student(String name, int age, double score) {
super(name, age);
this.score = score;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
@Override
public String say() {
return super.say()+score;
}
}
package D04;
public class Teacher extends Person{
private double salary;
public Teacher(String name, int age, double salary) {
super(name, age);
this.salary = salary;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
@Override
public String say() {
return super.say()+salary;
}
}
package D04;
public class Test {
public static void main(String[] args) {
Person[] a=new Person[5];
a[0]=new Person("陈士虎",20);
a[1]=new Student("陈士虎",21,100);
a[2]=new Student("陶雨",21,120);
a[3]=new Teacher("陶雨",32,2111);
a[4]=new Teacher("陈士虎",32,43333);
for(int i=0;i<a.length;i++)
{
//a【i】编译类型是Person,运行类型是根据实际情况由JVM来判断的
System.out.println(a[i].say());
}
}
}
//
陈士虎 20
//
陈士虎 21100.0
//
陶雨 21120.0
//
陶雨 322111.0
//
陈士虎 3243333.0

升级的代码

package D04;
public class Person {//父类
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String say()//返回名字和年龄
{
return name+" "+age;
}
}
package D04;
public class Student extends Person{
private double score;
public Student(String name, int age, double score) {
super(name, age);
this.score = score;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
@Override
public String say() {
return super.say()+score;
}
public void study()
{
System.out.println("学生"+getName()+"正在学习");
}
}
package D04;
public class Teacher extends Person{
private double salary;
public Teacher(String name, int age, double salary) {
super(name, age);
this.salary = salary;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
@Override
public String say() {
return super.say()+salary;
}
public void teach()
{
System.out.println("老师"+getName()+"正在教书");
}
}
package D04;
public class Test {
public static void main(String[] args) {
Person[] a=new Person[5];
a[0]=new Person("陈士虎",20);
a[1]=new Student("陈士虎",21,100);
a[2]=new Student("陶雨",21,120);
a[3]=new Teacher("陶雨",32,2111);
a[4]=new Teacher("陈士虎",32,43333);
for(int i=0;i<a.length;i++)
{
//类型判断和向下转型
if(a[i]instanceof Student)
{
((Student)a[i]).study();//向下转型
}else if (a[i]instanceof Teacher)
{
((Teacher) a[i]).teach();
}
else
{
System.out.println("类型有误");
}
}
}
}
//类型有误
//学生陈士虎正在学习
//学生陶雨正在学习
//老师陶雨正在教书
//老师陈士虎正在教书

多态参数

方法定义的形参类型为父类类型,实参类性允许为子类类型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LFcgBnh4-1640005795985)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211212104020103.png)]

package D04;
public class Test1
{
public static void main(String[] args)
{
Test1 c=new Test1();
Employee a=new Manage("陈士虎",20.0,100);
Employee b=new Work("陶雨",21.0);
c.showEmpAnnual(a);
c.showEmpAnnual(b);
c.testWork(a);
c.testWork(b);
}
public void showEmpAnnual(Employee a)
{
System.out.println( a.getName()+a.getAnnual());//动态绑定机制
}
public void testWork(Employee a)
{
if(a instanceof Manage)
{
((Manage) a).manage();
}
else if (a instanceof Work)
{
((Work) a).work();
}
}
}
class Employee
{
private String name;
private Double salary;
public Employee(String name, Double salary) {
this.name = name;
this.salary = salary;
}
public Double getAnnual()
{
return 12*salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getSalary() {
return salary;
}
public void setSalary(Double salary) {
this.salary = salary;
}
}
class Work extends Employee
{
public Work(String name, Double salary) {
super(name, salary);
}
public void work()
{
System.out.println("普通员工"+getName()+"正在工作");
}
@Override
public Double getAnnual() {//普通员工没有其他收入
return super.getAnnual();
}
}
class Manage extends Employee
{
private double bonus;
public Manage(String name, Double salary, double bonus) {
super(name, salary);
this.bonus = bonus;
}
public double getBonus() {
return bonus;
}
public void setBonus(double bonus) {
this.bonus = bonus;
}
public void manage()
{
System.out.println("经理"+getName()+"正在管理");
}
@Override
public Double getAnnual() {
return super.getAnnual()+bonus;
}
}
/*
陈士虎340.0
陶雨252.0
经理陈士虎正在管理
普通员工陶雨正在工作
*/

static关键字

静态属性

puclic class Student
{
private static int age;
private double score;
public static void main(String[] args)
{
Student student1 =new Student();
student1.age;
student1.score;
Student.age;//也可以,age叫类变量
}
}

static修饰的属性可以用者两种方法都可以调用,static修饰的变量也称类变量;

private static int age;

Student student1 =new Student();

1、 student1.age;

2、 Student.age;

静态方法

public class Student
{
public void run()
{
//非静态方法可以直接调用静态方法的所有内容;
go();
}
public static void go()
{
}
public static void main(String[] args)
{
//静态方法可以直接调用静态方法的所有内容;
go();//或者Student.go();
这是类.go();
//还有就是创这个类的一个实例,然后。go();
}
}

注意点:

  • 非静态方法可以直接调用静态方法的所有内容;
  • 静态方法可以直接调用静态方法的所有内容;

代码块

public class Person
{
//匿名代码块:可以赋一些初始值
System.out.println("匿名代码块")
}
static
{
System.out.println("静态代码块")
}
public Person
{
System.out.println("构造方法")
}
public static void main(String[] args)
{
Person person= new Person();
}
打印结果://静态代码块
//匿名代码块
//构造方法
public class Person
{
System.out.println("匿名代码块")
}
static
{
//只在第一次的时候执行
System.out.println("静态代码块")
}
public Person
{
System.out.println("构造方法")
}
public static void main(String[] args)
{
Person person= new Person();
System.out.println("-------------------------")
Person person1=new Person();
}
印结果:
//静态代码块
//匿名代码块
//构造方法
//
--------------------------------
//匿名代码块
//构造方法

匿名代码块、静态代码块、构造方法的执行顺序

  1. 对象一创建就先走匿名代码块

  2. 静态代码块(只会在第一次的时候执行

  3. 然后是构造方法

静态导入包

没有导入包的情况

//第一种方法
public class Test
{
public static void main(String[] args)
{
System.out.println(Math.random());
}
}
//第二种方法,导入静态的包;
import static java.lang.Math.random;
public class Test
{
public static void main(String[] args)
{
System.out.println(random());
}
}

用static修饰常量有什么好处?

好处就是:在创建类的多个对象时,用static修饰的常量在内存中只有一份拷贝。不用static修饰则可能有多份拷贝。

class A {
public static final String CONSTANT_A = "Hello";
public final String CONSTANT_B = "Hello";
}

创建A的多个对象时,CONSTANT_A在内存中只有1份拷贝,CONSTANT_B在内存中有多份拷贝。

类名.方法名。还没有实例化就可以调用,这就是static的好处了。
被static关键字修饰的方法或者变量不需要依赖于对象来进行访问,只要类被加载了,就可以通过类名去进行访问

final关键字

final在Java中是一个保留的关键字,可以声明成员变量、方法、类以及本地变量。一旦你将引用声明作final,你将不能改变这个引用了,编译器会检查代码,如果你试图将变量再次初始化的话,编译器会报编译错误。
一.final关键字基本用法

1.修饰变量

凡是对成员变量或者局部变量(在方法中的或者代码块中的变量称为本地变量)声明为final的都叫作final变量。final变量经常和static关键字一起使用,作为常量。

final修饰基本数据类型的变量时,必须赋予初始值且不能被改变,修饰引用变量时,该引用变量不能再指向其他对象

例如:

img

当final修饰基本数据类型变量时不赋予初始值以及引用变量指向其他对象时就会报错

当final修饰基本数据类型变量被改变时,就会报错

img

2.修饰方法

final也可以声明方法。方法前面加上final关键字,代表这个方法不可以被子类的方法重写。如果你认为一个方法的功能已经足够完整了,子类中不需要改变的话,你可以声明此方法为final。**final方法比非final方法要快,**因为在编译的时候已经静态绑定了,不需要在运行时再动态绑定。

img

3.修饰类

使用final来修饰的类叫作final类。final类通常功能是完整的,它们不能被继承。Java中有许多类是final的,譬如String, Interger以及其他包装类。

img

4.深入分析final关键字

1.被final修饰的对象内容是可变的

虽然对象被final修饰对象不可被继承,但其内容依然可以被改变

img

2.final关键字与static对比

static关键字修饰变量时,会使该变量在类加载时就会被初始化,不会因为对象的创建再次被加载,当变量被static 修饰时就代表该变量只会被初始化一次

img

例如图中所示,被static修饰的变量j,虽然创建两个对象,对值并没有变化。

项目–零钱通

第一个版本

package money;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
public class D01
{
public static void main(String[] args) {
//界面
Scanner scanner=new Scanner(System.in);
String key="";
boolean flag=true;
//收入
double key1=0.0;
double money=0.0;
String si="---------------零钱通明细------------------";
Date date=null;
SimpleDateFormat sad=new SimpleDateFormat("yyyy-yy-yy
yy:yy");//日期的格式化
//消费
double key2=0.0;
String name="";
//退出
String key3;
while (flag) {
System.out.println("n==============零钱通=============");
System.out.println("ttt 1零钱通明细");
System.out.println("ttt 2收益入账");
System.out.println("ttt 3消费");
System.out.println("ttt 4退
出");
System.out.println("请选择1-4:");
key=scanner.next();
switch (key)
{
case "1":
System.out.println(si);
break;
case "2":
System.out.println(" 2收益入账");
key1=scanner.nextDouble();
if (key1>=0) {
money += key1;//把所有的钱都放在money里
date = new Date();
si += "n" + "收益入账t" + "+" + key1 + "t" + sad.format(date) + "t" + money;
}
else
{
System.out.println(
"输入的金额错误,请重新输入金额:");
System.out.println(" 2收益入账");
key1=scanner.nextDouble();
money += key1;//把所有的钱都放在money里
date = new Date();
si += "n" + "收益入账t" + "+" + key1 + "t" + sad.format(date) + "t" + money;
}
break;
case "3":
System.out.println("
3消费");
System.out.println(" 输入金额");
key2 = scanner.nextDouble();
if(key2<=money) {
System.out.println(" 输入名称");
name = scanner.next();
money -= key2;
date = new Date();
si += "n" + name + "t-" + key2 + "t" + sad.format(date) + "t" + money;
}
else
{
System.out.println(
"输入的金额错误,请重新输入金额");
System.out.println(" 输入金额:");
key2=scanner.nextDouble();
System.out.println(" 输入名称");
name = scanner.next();
money -= key2;
date = new Date();
si += "n" + name + "t-" + key2 + "t" + sad.format(date) + "t" + money;
}
break;
case "4":
System.out.println("
你确定要退出吗?y/n");
key3=scanner.next();
if (key3.equals("y")){
System.out.println("
退
出");
flag=false;}
else if (key3.equals("n"));
else
{
System.out.println("输入错误");
}
break;
default:
System.out.println("输入错误,请重新输入");
}
}
}
}

第二版本oop

package money1;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
//oop面向对象编程
public class SmallChangeSysOOP
{
Scanner scanner=new Scanner(System.in);
String key="";
boolean flag=true;
//收入
double key1=0.0;
double money=0.0;
String si="---------------零钱通明细------------------";
Date date=null;
SimpleDateFormat sad=new SimpleDateFormat("yyyy-yy-yy
yy:yy");//日期的格式化
//消费
double key2=0.0;
String name="";
//退出
String key3;
public void mainMenu()
{
while (flag) {
System.out.println("n==============零钱通=============");
System.out.println("ttt 1零钱通明细");
System.out.println("ttt 2收益入账");
System.out.println("ttt 3消费");
System.out.println("ttt 4退
出");
System.out.println("请选择1-4:");
key=scanner.next();
switch (key)
{
case "1":
System.out.println(si);
break;
case "2":
details();
break;
case "3":
pay();
break;
case "4":
exit();
break;
default:
System.out.println("输入错误,请重新输入");
}
}
}
public void details()
{
System.out.println(" 2收益入账");
key1=scanner.nextDouble();
if (key1>=0) {
money += key1;//把所有的钱都放在money里
date = new Date();
si += "n" + "收益入账t" + "+" + key1 + "t" + sad.format(date) + "t" + money;
}
else
{
System.out.println(
"输入的金额错误,请重新输入金额:");
System.out.println(" 2收益入账");
key1=scanner.nextDouble();
money += key1;//把所有的钱都放在money里
date = new Date();
si += "n" + "收益入账t" + "+" + key1 + "t" + sad.format(date) + "t" + money;
return;
}
}
public void pay()
{
System.out.println("
3消费");
System.out.println(" 输入金额");
key2 = scanner.nextDouble();
if(key2<=money) {
System.out.println(" 输入名称");
name = scanner.next();
money -= key2;
date = new Date();
si += "n" + name + "t-" + key2 + "t" + sad.format(date) + "t" + money;
}
else
{
System.out.println(
"输入的金额错误,请重新输入金额");
System.out.println(" 输入金额:");
key2=scanner.nextDouble();
System.out.println(" 输入名称");
name = scanner.next();
money -= key2;
date = new Date();
si += "n" + name + "t-" + key2 + "t" + sad.format(date) + "t" + money;
}
}
public void exit()
{
System.out.println("
你确定要退出吗?y/n");
key3=scanner.next();
if (key3.equals("y")){
System.out.println("
退
出");
flag=false;}
else if (key3.equals("n"));
else
{
System.out.println("输入错误");
}
}
}
package money1;
public class Test {
public static void main(String[] args) {
new SmallChangeSysOOP().mainMenu();
}
}

本章作业

作业一

按照一个Person类(name,age,job),初始化Person对象数组,由3个person对象,并按照age从大到小进行排序,提示,使用冒泡排序

package housse;
public class Person {
private String name;
private int age;
private String job;
public Person(String name, int age, String job) {
this.name = name;
this.age = age;
this.job = job;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + ''' +
", age=" + age +
", job='" + job + ''' +
'}';
}
}
class Test
{
public static void main(String[] args) {
Person[] a=new Person[3];
a[0]=new Person("陈士虎",24,"上学");
a[1]=new Person("陈士虎1",23,"上学1");
a[2]=new Person("陈士虎2",25,"上学2");
for (int i=0;i< a.length;i++)
{
System.out.println(a[i]);
}//默认调用toString 方法
Person tem=null;
for (int i=0;i<a.length-1;i++)//外层循环
{
for (int j=0;j< a.length-1-i;j++)//内存循环
{
if (a[j].getAge()<a[j+1].getAge())
{
tem=a[j];
a[j]=a[j+1];
a[j+1]=tem;
}
}
}
System.out.println("------------------------------------------");
for (int i=0;i< a.length;i++)
{
System.out.println(a[i]);
}
}
}
/*
Person{name='陈士虎', age=24, job='上学'}
Person{name='陈士虎', age=24, job='上学'}
Person{name='陈士虎1', age=23, job='上学1'}
------------------------------------------
Person{name='陈士虎2', age=25, job='上学2'}
Person{name='陈士虎', age=24, job='上学'}
Person{name='陈士虎1', age=23, job='上学1'}
Person{name='陈士虎2', age=14, job='上学2'}
*/

作业二

写出四种访问修饰符和各自的访问权限

本类同包子类不同包
public
protected×
默认××
private×××

作业三

  1. 要求有属性”姓名name“,”年龄age“,“职称post””基本工资salary“
  2. 编写业务方法,introduce(),实现输出一个教师的信息
  3. 编写教师类的三个子类:教授类(professor),副教授类,讲师类。工资级别分别为:教授为1.3,副教授为1.2.讲师类1.1、在三个子类里面都重写父类的introduce()方法
  4. 定义并初始化一个老师对象,调用业务方法,实现对象基本信息的后台打印
package housse;
public class T1 {
private String name;
private int age;
private String post;
private Double salary;
public T1(String name, int age, String post, Double salary) {
this.name = name;
this.age = age;
this.post = post;
this.salary = salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getPost() {
return post;
}
public void setPost(String post) {
this.post = post;
}
public Double getSalary() {
return salary;
}
public void setSalary(Double salary) {
this.salary = salary;
}
public void introduce()
{
System.out.println(name+"+"+age+"+"+post+"+"+salary);
}
}
class T2 extends T1
{
public T2(String name, int age, String post, Double salary) {
super(name, age, post, salary);
}
@Override
public void introduce() {
System.out.println("教授");
System.out.println(getName()+"+"+getAge()+"+"+getPost()+"+"+getSalary()*1.3);
}
}
class T3 extends T1
{
public T3(String name, int age, String post, Double salary) {
super(name, age, post, salary);
}
@Override
public void introduce() {
System.out.println("副教授");
System.out.println(getName()+"+"+getAge()+"+"+getPost()+"+"+getSalary()*1.2);
}
}
class T4 extends T1
{
public T4(String name, int age, String post, Double salary) {
super(name, age, post, salary);
}
@Override
public void introduce() {
System.out.println("讲师");
System.out.println(getName()+"+"+getAge()+"+"+getPost()+"+"+getSalary()*1.1);
}
}
class Test1
{
public static void main(String[] args) {
T1 a=new T1("老师",21,"高级",32.0);
T1 b=new T2("教授",21,"最高级",32.0);
T1 c=new T3("副教授",21,"中级",32.0);
T1 d=new T4("讲师",21,"低级",32.0);
a.introduce();
b.introduce();
c.introduce();
d.introduce();
}
}
/*
老师+21+高级+32.0
教授
教授+21+最高级+41.6
副教授
副教授+21+中级+38.4
讲师
讲师+21+低级+35.2
*/

作业四

通过继承实现员工工资核算打印功能

父类:员工类

子类:部门经理类、普通员工类

  1. 部门经理工资=1000+单日工资x天数x等级(1.2)
  2. 普通员工工资=单日工资x天数x等级(1.0)
  3. 员工属性:姓名,单日工资,工作天数
  4. 员工方法(打印工资)
  5. 普通员工级部门经理都是员工子类,需要重写打印工资方法
  6. 定义并初始化普通员工对象,调用打印佛念稿子方法输出工资,定义并初始化部门经理对象,调用对打印方法输出工资

作业六

假定Grand、Father、和Son在同一个包,问:父类和子类中通过this和super

都可以调用那些属性

package housse;
public class weo1
{
public static void main(String[] args) {
}
}
class Grand
{
String name="A";
private int age=100;
public void g1(){}
}
class Father extends Grand
{
String id="001";
private double score;
public void f1()
{
//super可以访问那些成员(属性和方法)
//
super.name;
//
super.g1();
//
//this可以访问那些成员
//
this.id;
//
this.name;
//
this.g1();
//
this.score;
//
this.f1();
}
}
class Son extends Father{
String name = "BB";
public void g1() {
}
private void show()
{
//super可以访问那些成员(属性和方法)
//
super.id;
//
super.name;
//
super.f1();
//
super.g1();
//this可以访问那些成员
//
this.name;
//
this.id;
//
this.f1();
//
this.g1();
//
this.show();
}
}

作业七

写出程序结果

package housse;
public class D04 {
public static void main(String[] args) {
new Demo().test();
new Demo("john").test();
}
}
class Tes
{
String name="Rose";
Tes()
{
System.out.println("Test");
}
Tes(String name)
{
this.name=name;
}
}
class Demo extends Tes
{
String name="jack";
Demo()
{
super();
System.out.println("Demo");
}
Demo(String s)
{
super(s);
}
public void test()
{
System.out.println(super.name);
System.out.println(this.name);
}
}
/*
Test
Demo
Rose
Jack
john
Jack
*
* */

作业八

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d5aAsRS0-1640005795986)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211213205824520.png)]

package D02;
public class D04
{
public static void main(String[] args) {
//
BankAccount a=new BankAccount(1000);
//
a.deposit(1000);
//
System.out.println(a.getBalance());
//
a.withdraw(200);
//
System.out.println(a.getBalance());
d d = new d(1000);
d.deposit(100);
d.deposit(100);
d.deposit(100);
System.out.println(d.getBalance());
d.deposit(100);
System.out.println(d.getBalance());
d.lixi();
System.out.println(d.getBalance());
}
}
class BankAccount
{
private double balance;
//余额
public BankAccount(double initialBalance)
{
this.balance=initialBalance;
}
//存款
public void deposit(double amount)
{
balance+=amount;
}
//取款
public void
withdraw(double amount)
{
balance-=amount;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
}
class d extends BankAccount
{
private int count=3;
private double rate=0.01;//利率
public d(double initialBalance) {
super(initialBalance);
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public double getRate() {
return rate;
}
public void setRate(double rate) {
this.rate = rate;
}
@Override
public void deposit(double amount) {
//判断是否还可以免手续费
if (count>0) {
super.deposit(amount);
}else
{
super.deposit(amount-1);//1块转入银行账户
}
count--;
}
@Override
public void withdraw(double amount) {
if (count>0) {
super.withdraw(amount);
}else
{
super.deposit(amount+1);//1块转入银行账户
}
count--;
}
public void lixi()
{
//每个月初我们统计上个月的利息,同时将count=3;
count=3;
super.deposit(getBalance()*rate);
}
}
//1300.0
//1399.0
//1412.99

作业九

设计一个Point类,其x和y坐标可以通过构造器提供,提供一个子类,LabeledPoint,其构造器接受一个标签值和x,y坐标,比如:

new LabeledPoint(“Black Thursday”,1929,230.07),写出对应的的构造器即可

package D02;
public class Point {
private double x;
private double y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
}
class LabeledPoint extends Point
{
private String label;
public LabeledPoint(String label,double x, double y) {
super(x, y);
this.label = label;
}
}
class e
{
public static void main(String[] args) {
LabeledPoint a=new LabeledPoint("陈士虎",12,123);
}
}

作业十

编写Doctor类{name,age,job,gender,sal}相应的getter()和setter()方法,5个参数的构造器,重写父类的equals()方法:

public boolean equals(Object obj),并判断测试类中创建的两个对象是否相等,相等就判断属性是否相同

package D02;
import java.util.Objects;
public class Doctor {
private String name;
private int age;
private String job;
private String gender;
private double sal;
public Doctor(String name, int age, String job, String gender, double sal) {
this.name = name;
this.age = age;
this.job = job;
this.gender = gender;
this.sal = sal;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public double getSal() {
return sal;
}
public void setSal(double sal) {
this.sal = sal;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Doctor doctor = (Doctor) o;
return age == doctor.age && Double.compare(doctor.sal, sal) == 0 && Objects.equals(name, doctor.name) && Objects.equals(job, doctor.job) && Objects.equals(gender, doctor.gender);
}
@Override
public int hashCode() {
return Objects.hash(name, age, job, gender, sal);
}
}
class ta
{
public static void main(String[] args) {
Doctor a=new Doctor("陈士虎",12,"daf","dfas",42);
Doctor b=new Doctor("陈士虎",12,"daf","dfas",42);
System.out.println(a.equals(b));
}
}
//true

作业十一

11.现有Person类,里面有方法run、eat,Student类继承了Person类,并重写了run方法,自定义了study方法,试写出对象向上转型和向下转型的代码,并写出各自都可以调用哪些方法,并写出方法输出什么?

class Person { //父类
public void run() {System.out.println(“person run”);}public void eat() {System.out.printIn(“person eat”);}
class Student extends Person {//子类
public void run() {System.out.println(“student run”);}public void study(){System.out.printlIn(“student study.”);}答案∶
//向上转型∶ 父类的引用指向子类对象

Person p = new Student();

p.run();//student run

p.eat();//person eat
//向下转型∶把指向子类对象的父类引用,转成指向子类对象的子类引用

Student s = (Student)p;

package D02;
public class D06 {
public static void main(String[] args) {
Person a=new Student();
a.run();//此处study方法用不了
}
}
class Person { //父类
public void run() {
System.out.println("person run");
}
public void eat() {
System.out.println("person eat");
}
}
class Student extends Person {//子类public void run() {
System.out.println("student run");
}
public void study() {
System.out.println("student study.");
}
}

作业十二

说出==和euqals的区别(简答)

名称概念由于基本数据类型用于引用类型
==比较运算符用于基本数据类型,可以判断值是否相等可以,判断两个对象是否相等
equalsObject类的方法,java类都可以使用euqals不可以可以,默认是判断两个对象是否相等,但是子类往往重写该方法,比较对象的属性是否相等,

作业十三

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HUjHJxWh-1640005795987)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211214101129865.png)]

package D05;
public class Test {
public static void main(String[] args) {
Person a=new Student("陈士虎","男",23,34);
//
Person a1=new Student("陶雨","女",21,23);
Person b=new Teacher("陈士虎","男",23,1);
//
Person b1=new Teacher("陶雨","女",21,2);
//定义多态数组,里面保存2个学生和2个老师,要求按年龄从高到底排序
Person[] r=new Person[4];
r[0]=new Student("陈士虎","男",15,34);
r[1]=new Student("士大夫","男",23,34);
r[2]=new Teacher("发生的","男",22,1);
r[3]=new Teacher("陶雨","女",21,2);
//创建对象
Test c=new Test();
c.s(r);
System.out.println("排序后的数组-----------------");
for (Person be:r) {
System.out.println(be);
}
//定义方法,形参为Person类型,功能,调用学生的study或教师的teach方法
System.out.println("---------------------------------");
for (Person be:r) {//加强for循环
c.c(be);
}
}
//冒泡排序,完成年龄从高到底排序
public void s(Person[] r)
{
Person tnm=null;
for (int i=0;i<r.length-1;i++)
{
for (int j=0;j< r.length-1-i;j++)
{
if (r[j].getAge()<r[j+1].getAge())
//判断条件,注意这里的条件可以根据需要,变化
{
tnm = r[j];
r[j] = r[j + 1];
r[j + 1] = tnm;
}
}
}
}
//定义方法,形参为Person类型,功能,调用学生的study或教师的teach方法
public void c(Person c)
{
if (c instanceof Student)
{
((Student) c).study();
}
else if (c instanceof Teacher)
{
((Teacher) c).teach();
}
}
}
//
class Student extends Person
{
private int id;
public Student(String name, String sex, int age, int id) {
super(name,sex,age);
this.id = id;
}
public void study()
{
System.out.println(getName()+"承诺,我会好好学习");
}
public String play()
{
return super.play()+"足球";
}
@Override
public void infor() {
System.out.println("学生的信息:");
super.infor();
study();
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
'}'+super.toString();
}
}
//
class Teacher extends Person
{
private int workage;
public Teacher(String name, String sex, int age, int workage) {
super(name,sex,age);
this.workage = workage;
}
public void teach()
{
System.out.println(getName()+"承诺,我会好好教学");
}
public String play()
{
return super.play()+"象棋";
}
@Override
public void infor() {
System.out.println("老师的信息:");
super.infor();
teach();
}
@Override
public String toString() {
return "Teacher{" +
"workage=" + workage +
'}'+super.toString();
}
}
//
class Person
{
private String name;
private String sex;
private int age;
public Person(String name, String sex, int age) {
this.name = name;
this.sex = sex;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String play()
{
return name+"爱玩";
}
public void infor()
{
System.out.println("姓名:"+getName());
System.out.println("年龄:"+getAge());
System.out.println("性别:"+getSex());
System.out.println(play());
}
@Override
public String toString() {
return "Person{" +
"name='" + name + ''' +
", sex='" + sex + ''' +
", age=" + age +
'}';
}
}
/*
排序后的数组-----------------
Student{id=34}Person{name='士大夫', sex='男', age=23}
Teacher{workage=1}Person{name='发生的', sex='男', age=22}
Teacher{workage=2}Person{name='陶雨', sex='女', age=21}
Student{id=34}Person{name='陈士虎', sex='男', age=15}
---------------------------------
士大夫承诺,我会好好学习
发生的承诺,我会好好教学
陶雨承诺,我会好好教学
陈士虎承诺,我会好好学习
*/

作业十四

C c=new C();;

输出的内容?

package D02;
public class Te {
public static void main(String[] args) {
C c=new C();
}
}
class A
{
public A()
{
System.out.println("我是A类");
}
public A(String a)
{
System.out.println("我是A类有参");
}
}
class B extends A{
public B()
{
System.out.println("我是B类的无参构造");
}
public B(String name)
{
System.out.println(name+"我是B类的有参构造");
}
}
class C extends B
{
public C()
{
this("hello");
System.out.println("我是C类的无参构造");
}
public C(String name)
{
super("hahah");
System.out.println("我是C类的有参构造");
}
}
/*
我是A类
hahah我是B类的有参构造
我是C类的有参构造
我是C类的无参构造
*/

作业十五

什么是多态,多态具体体现有那些?

多态:方法或对象具有多种形态,是oop的第三大特征,是建立在封装和继承的基础之上

具体体现

1.方法多态

  1. 重载体现多态
  2. 重写体现多态

2.对象多态

  1. 对象的编译类型和运行类型可以不一致,编译类型在定义时,就确定了,不能变化
  2. 对象的运行类型时可以变化的,可以通过getClass()来查看运行
  3. 编译类型看定义时,=号的左边,运行类型=号右边

作业十六

java的动态绑定机制

  1. 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
  2. 当调用对象属性时,没有动态绑定,哪里声明,哪里使用

项目–房屋出租系统

三部曲

明确完成功能—》思路分析–》代码实现

房子的类

package D02.home.util.domain;
//房子的类
public class House {
private int id;
private String name;
private String phone;
private String address;
private int rent;
private String state;
public House(int id, String name, String phone, String address, int rent, String state) {
this.id = id;
this.name = name;
this.phone = phone;
this.address = address;
this.rent = rent;
this.state = state;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getRent() {
return rent;
}
public void setRent(int rent) {
this.rent = rent;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
//为了方便的输出对象信息,我们实现toString
@Override
public String toString() {
return id + "tt" + name + "tt"
+ phone + "tt"
+ address + "tt"
+ rent + "tt"
+ state;
}
}

工具类

package D02.home.util;
/**
工具类的作用:
处理各种情况的用户输入,并且能够按照程序员的需求,得到用户的控制台输入。
*/
import java.util.Scanner;
/**
工具类
*/
public class Utility {
//静态属性。。。
private static Scanner scanner = new Scanner(System.in);
/**
* 功能:读取键盘输入的一个菜单选项,值:1——5的范围
* @return 1——5
*/
public static char readMenuSelection() {
char c;
for (; ; ) {
String str = readKeyBoard(1, false);//包含一个字符的字符串
c = str.charAt(0);//将字符串转换成字符char类型
if (c != '1' && c != '2' &&
c != '3' && c != '4' && c != '5') {
System.out.print("选择错误,请重新输入:");
} else break;
}
return c;
}
/**
* 功能:读取键盘输入的一个字符
* @return 一个字符
*/
public static char readChar() {
String str = readKeyBoard(1, false);//就是一个字符
return str.charAt(0);
}
/**
* 功能:读取键盘输入的一个字符,如果直接按回车,则返回指定的默认值;否则返回输入的那个字符
* @param defaultValue 指定的默认值
* @return 默认值或输入的字符
*/
public static char readChar(char defaultValue) {
String str = readKeyBoard(1, true);//要么是空字符串,要么是一个字符
return (str.length() == 0) ? defaultValue : str.charAt(0);
}
/**
* 功能:读取键盘输入的整型,长度小于2位
* @return 整数
*/
public static int readInt() {
int n;
for (; ; ) {
String str = readKeyBoard(2, false);//一个整数,长度<=2位
try {
n = Integer.parseInt(str);//将字符串转换成整数
break;
} catch (NumberFormatException e) {
System.out.print("数字输入错误,请重新输入:");
}
}
return n;
}
/**
* 功能:读取键盘输入的 整数或默认值,如果直接回车,则返回默认值,否则返回输入的整数
* @param defaultValue 指定的默认值
* @return 整数或默认值
*/
public static int readInt(int defaultValue) {
int n;
for (; ; ) {
String str = readKeyBoard(10, true);
if (str.equals("")) {
return defaultValue;
}
//异常处理...
try {
n = Integer.parseInt(str);
break;
} catch (NumberFormatException e) {
System.out.print("数字输入错误,请重新输入:");
}
}
return n;
}
/**
* 功能:读取键盘输入的指定长度的字符串
* @param limit 限制的长度
* @return 指定长度的字符串
*/
public static String readString(int limit) {
return readKeyBoard(limit, false);
}
/**
* 功能:读取键盘输入的指定长度的字符串或默认值,如果直接回车,返回默认值,否则返回字符串
* @param limit 限制的长度
* @param defaultValue 指定的默认值
* @return 指定长度的字符串
*/
public static String readString(int limit, String defaultValue) {
String str = readKeyBoard(limit, true);
return str.equals("")? defaultValue : str;
}
/**
* 功能:读取键盘输入的确认选项,Y或N
* 将小的功能,封装到一个方法中.
* @return Y或N
*/
public static char readConfirmSelection() {
System.out.println("请输入你的选择(Y/N)");
char c;
for (; ; ) {//无限循环
//在这里,将接受到字符,转成了大写字母
//y => Y n=>N
String str = readKeyBoard(1, false).toUpperCase();
c = str.charAt(0);
if (c == 'Y' || c == 'N') {
break;
} else {
System.out.print("选择错误,请重新输入:");
}
}
return c;
}
/**
* 功能: 读取一个字符串
* @param limit 读取的长度
* @param blankReturn 如果为true ,表示 可以读空字符串。
*
如果为false表示 不能读空字符串。
*
* 如果输入为空,或者输入大于limit的长度,就会提示重新输入。
* @return
*/
private static String readKeyBoard(int limit, boolean blankReturn) {
//定义了字符串
String line = "";
//scanner.hasNextLine() 判断有没有下一行
while (scanner.hasNextLine()) {
line = scanner.nextLine();//读取这一行
//如果line.length=0, 即用户没有输入任何内容,直接回车
if (line.length() == 0) {
if (blankReturn) return line;//如果blankReturn=true,可以返回空串
else continue; //如果blankReturn=false,不接受空串,必须输入内容
}
//如果用户输入的内容大于了 limit,就提示重写输入
//如果用户如的内容 >0 <= limit ,我就接受
if (line.length() < 1 || line.length() > limit) {
System.out.print("输入长度(不能大于" + limit + ")错误,请重新输入:");
continue;
}
break;
}
return line;
}
}

实现类

package D02.home.util;
//实现类
import D02.home.util.HouseView.MainMenu;
public class App {
public static void main(String[] args) {
new MainMenu().menu();
}
}

主菜单类

package D02.home.util.HouseView;
//主菜单
import D02.home.util.Service.HouseService;
import D02.home.util.Utility;
import D02.home.util.domain.House;
public class MainMenu {
private boolean loop=true;
private char key=' ';//把key设置为空字符
//编写listHouses()显示房屋列表
private HouseService houseService=new HouseService(3);//设置house数组的长度
public void listHouses()
{
System.out.println("----------------房屋列表----------------");
System.out.println("编号tt房主tt电话tt地址tt月租tt状态(已出租/未出租)");
House[] a=houseService.list();//把
for (int i=0;i<a.length;i++)
{
if(a[i]==null) {break;
}
else {
System.out.println(a[i]);
}
}
System.out.println("----------------房屋列表显示完毕----------------");
}
//用来添加房屋数据
public void addHouse()
{
System.out.println("----------------添加房屋----------------");
System.out.println("请输入姓名:");
String name=Utility.readString(100);
System.out.println("请输入电话");
String da= Utility.readString(100);
System.out.println("请输入地址");
String stress= Utility.readString(100);
System.out.println("请输入月租");
int
rent =Utility.readInt();
System.out.println("请输入状态(已出租/未出租)");
String date=Utility.readString(100);
//创建一个新的House对象,注意id是系统分配的
House b=new House(0,name,da,stress,rent,date);
if (houseService.add(b)==false)
{
System.out.println("----------------房屋添加失败----------------");
}
else
{System.out.println("----------------房屋添加成功----------------");
}
}
//用来删除房源的方法
public void deteleHouse()
{
System.out.println("----------------删除房屋----------------");
System.out.println("请选择待删除的房屋编号(-1退出)");
int dbianhao=Utility.readInt();
if(dbianhao==-1)
{
System.out.println("----------------放弃删除房屋----------------");
return ;
}
System.out.println("确认是否删除(Y/N):请小心选择");
String dchoice=Utility.readString(100);
if (dchoice.equals("y"))//引用数据类型必须要用equals方法
{
if (houseService.delete(dbianhao))
{
System.out.println("----------------删除房屋成功----------------");
}
else
{
System.out.println("----------------房屋编号不存在----------------");
}
}
else
{
System.out.println("----------------放弃删除房屋----------------");
}
}
//退出的方法
public void exitHouse()
{
System.out.println("确认是否退出(Y/N):");
String exit=Utility.readString(100);
if (exit.equals("y"))
{
loop=false;
}
else if (exit.equals("n"))
{
}
else {
while (exit.equals("y")||exit.equals("n")){
System.out.println("确认是否退出(Y/N):");
exit=Utility.readString(100);
}
}
}
//查找方法
public void search()
{
System.out.println("请输入查找房屋的id:");
int id=Utility.readInt();
houseService.sea(id);
}
//修改的方法
public void update()
{
System.out.println("请输入修改房屋的id:");
int id=Utility.readInt();
System.out.println("请输入修改房屋的姓名:");
String
h=Utility.readString(1000);
System.out.println("请输入修改房屋的电话:");
String
pho=Utility.readString(1000);
System.out.println("请输入修改房屋的地址:");
String
adr=Utility.readString(1000);
System.out.println("请输入修改房屋的月租:");
int
rent=Utility.readInt();
System.out.println("请输入修改房屋的状态:");
String
date=Utility.readString(1000);
houseService.up(id,h,pho,adr,rent,date);
System.out.println("--------------修改成功---------------");
}
//主菜单
public void menu()
{
do {
System.out.println("----------------房屋出租系统----------------");
System.out.println("ttt1 新 增 房 源");
System.out.println("ttt2 查 找 房 屋");
System.out.println("ttt3 删 除 房 屋");
System.out.println("ttt4 修 改 房 屋 信 息");
System.out.println("ttt5 房 屋 列 表");
System.out.println("ttt6 退
出");
System.out.println("请输入(1-6):");
key= Utility.readChar();
switch (key)
{
case '1':addHouse();
break;
case '2':search();
break;
case '3':
deteleHouse();
break;
case '4':update();
break;
case '5':
listHouses();
break;
case '6':
exitHouse();
break;
default:
System.out.println("输入有误");
System.out.println("请重新输入(1-6):");
key= Utility.readChar();
}
}while (loop);
}
}

服务类

package D02.home.util.Service;
//服务类
import D02.home.util.domain.House;
public class HouseService {
private House[] house;//只是设置一个空数组而已。保存House对象
private int count = 1;
private int size;
private int housecount = 1;//记录当前的房屋数量
public HouseService(int size) {//size用来设置数组的长度
house = new House[size];
house[0] = new House(1, "陈士虎", "234", "江苏", 10, "为出租");
}
public House[] list()//返回house数组的所有内容
{
return house;
}
//添加的方法
public boolean add(House newHouse) {
//判断是否还可以继续添加
if (housecount == house.length) {
System.out.println("已满。。。。。");
return false;
} else {
house[housecount++] = newHouse;
newHouse.setId(++count);
return true;
}
}
//删除的方法
public boolean delete(int a)
{
//应当先找到要删除的房屋信息对应的下标
//一定要搞清楚,下标和房屋的编号不是一回事
int index = -1;
for (int i = 0; i < housecount; i++)
{
if (a == house[i].getId())
{
{//要删除的房屋对应的id,是数组下标为i的元素
index = i;//使用index记录i
}
}
}
if (index == -1)
{
return false;
}
for (int t = index; t <= housecount-1; t++)
{
house[t] = house[t + 1];
}
//把当有存在的房屋信息的最后一个 设置为null
house[--housecount]=null;
return true;
}
//根据id来查找房屋信息
public void sea(int a)
{
//这是用id来找到数组中对应的下标
即i
//这里的id也可以换成其他的属性
int index = -1;
for (int i = 0; i < housecount; i++)
{
if (a == house[i].getId())
{
{//要删除的房屋对应的id,是数组下标为i的元素
index = i;//使用index记录i
}
}
}
//如果
if(index==-1)
{
System.out.println("输入的id有误:");
return;
}
if (index<=housecount)
{
System.out.println(house[index]);
}
}
//根据id修改房屋的信息
public void up(int a,String
h,String
pho,String
adr,int
rent,String
date)
{
int index = -1;
for (int i = 0; i < housecount; i++)
{
if (a == house[i].getId())
{
{//要删除的房屋对应的id,是数组下标为i的元素
index = i;//使用index记录i
}
}
}
if(index==-1)
{
System.out.println("输入的id有误:");
return;
}
if (index<=housecount)
{
house[index].setName(h);
house[index].setPhone(pho);
house[index].setAddress(adr);
house[index].setRent(rent);
house[index].setState(date);
}
}
}

面向对象(高级)

类变量和类方法

快速入门

类变量—提出的问题

提出的问题的主要目的就是让大家思考解决之道,从而引出要讲的知识点

问题:

有一群小孩再玩堆雪人,不时有新的小孩加入,请问如何知道现在共有多少个人再玩?,编写程序解决

package D06;
public class stati {
public static void main(String[] args) {
int count=0;
Child child1 = new Child("老虎");
child1.join();
count++;
Child child2 = new Child("老虎1");
child2.join();
count++;
Child child3 = new Child("老虎2");
child3.join();
count++;
System.out.println("共有"+count+"个人加入游戏");
}
}
class Child
{
private String name;
public Child(String name) {
this.name = name;
}
public void join()
{
System.out.println(name+"加入游戏");
}
}
//老虎加入游戏
//老虎1加入游戏
//老虎2加入游戏
//共有3个人加入游戏

传统的代码的问题分析:

  1. count是一个独立于对象,很尴尬
  2. 以后我们其他类的其他方法怎么访问count,没有使用到oop
  3. 因此,我们引出类变量/静态变量

改进代码

package D06;
public class stati {
public static void main(String[] args) {
Child child1 = new Child("老虎");
child1.join();
child1.count++;
Child child2 = new Child("老虎1");
child2.join();
child1.count++;
Child child3 = new Child("老虎2");
child3.join();
child1.count++;
//类变量可以通过类名来访问
System.out.println("共有"+Child.count+"个人加入游戏");
System.out.println("---------------------------------");
System.out.println(" child1.count="+child1.count);
System.out.println(" child2.count="+child2.count);
System.out.println(" child3.count="+child3.count);
}
}
class Child
{
private String name;
//定义一个变量count,是一个类变量(静态变量)static静态
//该变量最大的特点就是会被被Child类的所有的对象实例共享
public static int count=0;
public Child(String name) {
this.name = name;
}
public void join()
{
System.out.println(name+"加入游戏");
}
}

类变量内存布局

韩顺平 375集可以再看看

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5TeuzQ5e-1640005795988)(/…/typora图片/image-20211217100652522.png)]

重点记住:

不管static变量在哪里:

  1. static变量是同一个类所有对象共享
  2. static 类变量,再类加载的时候就生成了

系统学习

什么是类变量

类变量也叫静态变量/静态属性,是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的也是同一个变量这个从前面的图也可以看出来

如何定义类变量

定义语法

访问修饰符 static 数据类型 变量名;(推荐)

static 访问修饰符 数据类型 变量名;

如何访问类变量

类名.类变量(推荐)

或对象名.类变量名

(静态变量的访问修饰符的访问权限和范围和普通属性是一样的)

package D06;
public class D01 {
public static void main(String[] args) {
//说明,类变量是随着类的加载而创建的,所以即使没有创建对象实例也可以访问
System.out.println(A.name);
A a = new A();
//通过对象名.类变量名
System.out.println(a.name);
}
}
class A
{
//类变量
//类变量的访问,必须要遵守相关的访问权限
//如果下面的修饰符是private,上面的代码就是错误的
public static String name="陶雨最捞";
//普通变量 或者 普通成员变量
或者 非静态属性
或者
非静态成员变量
private int nu=10;
}
/*
陶雨最捞
陶雨最捞
*/

类变量使用注意事项和细节

  1. 什么时候需要用类变量

    当我们需要让某个类的所有对象都共享一个变量时,就可以考虑使用类变量(静态变量)

    比如:定义学术类,统计所有学生共交多少钱,

  2. 类变量与实例变量(普通属性)区别

    类变量是该类的所有对象共享的,而实例变量是每个对象独享的

  3. 加上static称为类变量或静态变量,否则称为实例变量/普通变量/非静态变量

  4. 类变量可以通过,类名.类变量名或 对像名.类变量名来访问,但java设计者推荐我们使用类名.类变量名方式访问(前提是满足访问修饰符的访问权限和范围)

  5. 实例变量不能通过 类名.类变量名 方式访问

  6. 类变量是在类加载时就初始化了,也就说,即使,你没有创建对象,只要类加载了。就可以使用类变量了

  7. 类变量的生命周期是随着类的加载开始的,随着类消亡而消亡

类方法

类方法也叫静态方法

形式如下:

访问修饰符 static 数据返回类型 方法名(){}(推荐)

static 访问修饰符 数据返回类型 方法名(){}

类方法的调用

使用方式:类名.类方法名或者对象名.类方法名(前提是 满足访问修饰符的访问权限和范围)

类方法中不涉及到任何和对象相关的成员,则可以将方法设计成静态方法,提高开发效率

小结

在程序员实际开发,往往会将一些通用的方法,设计成静态方法,这样我们不需要创建对象就可以使用了,比如打印以为数组,冒泡排序

package com.hspedu.static_;
public class StaticMethod {
public static void main(String[] args) {
//创建 2 个学生对象,叫学费 
Stu tom = new Stu("tom");
//tom.payFee(100); 
Stu.payFee(100);//对不对?对 
Stu mary = new Stu("mary");
//mary.payFee(200); 
Stu.payFee(200);//对 
//输出当前收到的总学费 
Stu.showFee();//300 
//如果我们希望不创建实例,也可以调用某个方法(即当做工具来使用) 
//这时,把方法做成静态方法时非常合适 
System.out.println("9 开平方的结果是=" + Math.sqrt(9));
System.out.println(MyTools.calSum(10, 30));
}
}
//开发自己的工具类时,可以将方法做成静态的,方便调用 
class MyTools {
//求出两个数的和 
public static double calSum(double n1, double n2) {
return n1 + n2;
}
//可以写出很多这样的工具方法... 
}
class Stu {
private String name;//普通成员 
//定义一个静态变量,来累积学生的学费 
private static double fee = 0;
public Stu(String name) {
this.name = name;
}
//说明 
//1. 当方法使用了 static 修饰后,该方法就是静态方法 
//2. 静态方法就可以访问静态属性/变量 
public static void payFee(double fee) {
Stu.fee += fee;//累积到 
}
public static void showFee() {
System.out.println("总学费有:" + Stu.fee);
}
}
类方法使用注意事项和细节讨论
  1. 类方法和普通方法都是随着类的加载而加载,将结构信息存储在方法区:

    类方法中无this的参数

    普通方法中隐含着this的参数

  2. 类方法可以通过类名调用,也可以通过对象名调用

  3. 普通方法和对象有关,需要通过对象名调用,比如 对象名.方法名(参数),不能通过类名调用

  4. 类方法中不允许使用和对象有关的关键字,比如this和super。普通方法(成员方法)可以

  5. 类方法(静态方法)中,只能访问静态变量或静态方法,

  6. 普通成员方法,既可以访问普通变量(方法),也可以访问静态变量(方法)

小结: 静态方法,只能访问静态的成员,非静态的方法,可以访问静态成员和非静态的成员(必须遵守访问权限)

课堂练习

题一

输出的是什么

public class D07
{
public static void main(String[] args) {
new Test().count();
new Test().count();
System.out.println(Test.count);
}
}
class Test
{
static int count=9;
public void count()
{
//这里是先输出 再++
System.out.println("count="+(count++));
}
}
//
count=9

//
count=10
//
11

题二

看看下面代码有没有错误,如果有就修改,看看输出什么

public class D07
{
public static void main(String[] args) {
System.out.println("total=" +Person.getTotalPerson());
Person p1 = new Person();
System.out.println("total=" +Person.getTotalPerson());
}
}
class Person
{
private int id;
private static int total=0;
public static int getTotalPerson()
{
return total;
}
public Person()
{
total++;
id=total;
}
}
//
total=0
//
total=1

题三

看看下面的代码有没有错误,如果有错误,就修改,看看total等于多少

package D06;
public class D02 {
public static void main(String[] args) {
Person.setTotalPerson(3);
System.out.println(Person.ru());
new Person();
System.out.println(Person.ru());
}
}
class Person
{
private int id;
private static int total=0;
public static void setTotalPerson(int total)
{
//this.total=total;//错误,因为在static方法中,不可以使用this关键字
Person.total=total;
}
public Person()//构造器
{
total++;
id=total;
}
public static int ru()//因为total是私有属性,所以要用一个方法来返回total的值
{
return total;
}
}
//3
//4

理解main方法语法

深入理解main方法

解释main方法的形式:public static void main(String[] args){ }

  1. java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public
  2. java虚拟机在执行main()方法时不必创建对象,所以该方法必须时static
  3. 该方法接受String类型的数组参数,该数组中保存执行java命令时传递给所运行的类的参数,
  4. java 执行的程序

特别提示;

  1. 在main()方法中,我们可以直接调用main方法所在类的静态方法或静态属性
  2. 但是,不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员
package com.hspedu.main_;
public class Main01 {
//静态的变量/属性 
private static String name = "韩顺平教育";
//非静态的变量/属性 
private int n1 = 10000;
//静态方法 
public static void hi() {
System.out.println("Main01 的 hi 方法");389**韩顺平循序渐进学** **Java** **零基础**
}
//非静态方法 
public void cry() {
System.out.println("Main01 的 cry 方法");
}
public static void main(String[] args) {
//可以直接使用 name 
//1. 静态方法 main 可以访问本类的静态成员 
System.out.println("name=" + name);
hi();
//2. 静态方法 main 不可以访问本类的非静态成员 
//System.out.println("n1=" + n1);//错误 
//cry(); 
//3. 静态方法 main 要访问本类的非静态成员,需要先创建对象 , 再调用即可 
Main01 main01 = new Main01();
System.out.println(main01.n1);//ok 
main01.cry();
}
}

代码块

代码块快速入门

基本介绍

代码块又称初始化块,属于类中的成员(即 是类的一部分),类似于方法,将逻辑语句封装在方法体中,通过,{}包围起来

但和方法不一样,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显示调用,而是加载类时,或这创建对象时隐式调用

基本语法

【修饰符】{ };

注意:

  1. 修饰符可选,要写的话,也只能写 static
  2. 代码块分为两类,使用static 修饰的叫静态代码块,没有static修饰的,叫普通代码块
  3. 逻辑语句可以为任何逻辑语句(输入、输出、方法调用、循环、判断)
  4. 号可以写上。也可以省略

代码块的好处

  1. 相当于另外一种形式的构造器(对构造器的补充机制),可以做初始化的操作
  2. 场景:如果多个构造器中都有重复的语句,可以抽取到初始化块中,提高代码的重用性
  3. 代码块的快速入门如下
package com.hspedu.codeblock_;
public class CodeBlock01 {
public static void main(String[] args) {
Movie movie = new Movie("你好,李焕英");
System.out.println("===============");
Movie movie2 = new Movie("唐探 3", 100, "陈思诚");
}392**韩顺平循序渐进学** **Java** **零基础**
}
class Movie {
private String name;
private double price;
private String director;
//3 个构造器-》重载 
//老韩解读 
//(1) 下面的三个构造器都有相同的语句 
//(2) 这样代码看起来比较冗余 
//(3) 这时我们可以把相同的语句,放入到一个代码块中,即可 
//(4) 这样当我们不管调用哪个构造器,创建对象,都会先调用代码块的内容 
//(5) 代码块调用的顺序优先于构造器.. 
{
System.out.println("电影屏幕打开...");
System.out.println("广告开始...");
System.out.println("电影正是开始...");
};
public Movie(String name) {
System.out.println("Movie(String name) 被调用...");
this.name = name;
}
public Movie(String name, double price) {393**韩顺平循序渐进学** **Java** **零基础**
this.name = name;
this.price = price;
}
public Movie(String name, double price, String director) {
System.out.println("Movie(String name, double price, String director) 被调用...");
this.name = name;
this.price = price;
this.director = director;
}
}

代码块使用注意事项和细节讨论(重要)

  1. static代码块也叫静态代码块,作用就是对类进行初始化,而且它随着类的加载而执行,并且只会执行一次,如果是普通代码块,没创建一个对象,就执行

  2. 类什么时候被加载**(重要背)**

    • 创建对象实例时(new)
    • 创建子类对象实例,父类也会加载
    • 使用类的静态成员时(静态属性 ,静态方法)
  3. 普通的代码块,在创建对象实例时,会被隐式的调用。

    被创建一次,就会调用一次

    如果只是实用类的静态成员时,普通代码并不会执行

小结:

  1. static代码块是类加载时,执行,指挥执行一次
  2. 普通代码块是在创建对象时调用的,创建一次,调用一次
  3. 类加载的3种情况,需要记住
package com.hspedu.codeblock_;
public class CodeBlockDetail01 {
public static void main(String[] args) {
//类被加载的情况举例 
//1. 创建对象实例时(new) 
// AA aa = new AA(); 
//2. 创建子类对象实例,父类也会被加载, 而且,父类先被加载,子类后被加载 
// AA aa2 = new AA(); 
//3. 使用类的静态成员时(静态属性,静态方法) 
// System.out.println(Cat.n1); 
//static 代码块,是在类加载时,执行的,而且只会执行一次. 
// 
DD dd = new DD();
// 
DD dd1 = new DD();
//普通的代码块,在创建对象实例时,会被隐式的调用。 
// 被创建一次,就会调用一次。 
// 如果只是使用类的静态成员时,普通代码块并不会执行 
System.out.println(DD.n1);//8888, 静态模块块一定会执行 
}
}
class DD {
public static int n1 = 8888;//静态属性 
//静态代码块 
static {
System.out.println("DD 的静态代码 1 被执行...");// 
}
//普通代码块, 在 new 对象时,被调用,而且是每创建一个对象,就调用一次**韩顺平循序渐进学** **Java** **零基础** 
//可以这样简单的,理解 普通代码块是构造器的补充 
{
System.out.println("DD 的普通代码块...");
}
}
class Animal {
//静态代码块 
static {
System.out.println("Animal 的静态代码 1 被执行...");// 
}
}
class Cat extends Animal {
public static int n1 = 999;//静态属性 
//静态代码块 
static {
System.out.println("Cat 的静态代码 1 被执行...");// 
}
}
class BB {
//静态代码块 
static {
System.out.println("BB 的静态代码 1 被执行...");//1 
}
}
class AA extends BB {
//静态代码块 396**韩顺平循序渐进学** **Java** **零基础**
static {
System.out.println("AA 的静态代码 1 被执行...");//2 
}
}
  1. 创建一个对象时,在一个类 调用顺序是:(重点,难点):

    1. 调用静态代码块和静态属性初始化(注意 :静态代码块和静态属性初始化调用的优先级一样,如果有多个静态代码块和静态变量初始化,则按他们定义的顺序调用)
    package D06;
    public class D03 {
    public static void main(String[] args) {
    D d = new D();
    }
    }
    class D
    {
    //静态属性初始化
    public static int s=get();
    static
    {//静态代码块
    System.out.println("调用static静态代码块");
    }
    public static int get()
    {
    System.out.println("get被调用");
    return 100;
    }
    }
    /*
    get被调用
    调用static静态代码块
    */
    //按照顺序类打印,
    
    1. 调用普通代码块和普通属性的初始化(注意:普通代码块和普通属性初始化调用的优先级一样,如果有多个普通代码块和多个普通属性初始化,作为按他们定义顺序调用)
    package D06;
    public class D03 {
    public static void main(String[] args) {
    D d = new D();
    }
    }
    class D
    {
    //普通属性初始化
    private int n=get1();
    //静态属性初始化
    public static int s=get();
    static
    {//静态代码块
    System.out.println("调用static静态代码块");
    }
    //普通代码块
    {
    System.out.println("调用普通代码块");
    }
    public static int get()
    {
    System.out.println("get被调用");
    return 100;
    }
    public int get1()
    {
    System.out.println("get1被调用");
    return 19;
    }
    }
    /*
    get被调用
    调用static静态代码块
    get1被调用
    调用普通代码块
    */
    
    1. 调用构造方法

      package D06;
      public class D03 {
      public static void main(String[] args) {
      D d = new D();
      }
      }
      class D
      {
      //普通属性初始化
      private int n=get1();
      //静态属性初始化
      public static int s=get();
      static
      {//静态代码块
      System.out.println("调用static静态代码块");
      }
      //普通代码块
      {
      System.out.println("调用普通代码块");
      }
      public static int get()
      {
      System.out.println("get被调用");
      return 100;
      }
      public int get1()
      {
      System.out.println("get1被调用");
      return 19;
      }
      //无参构造器
      public D()
      {
      System.out.println("调用无参构造器");
      }
      }
      
  2. 构造方法(构造器)的最前面其实隐含了super()和调用普通代码块,静态相关的代码块,属性初始化,在类加载时,就执行完毕

    因此优先于构造器和普通代码块执行的

    public A()
    {
    //这里有隐藏的执行要求
    //1.super();这个知识点,在前面讲解继承的时候,
    //2.调用普通代码块
    System.out.println("ok");
    }
    
package D06;
public class Do4 {
public static void main(String[] args) {
new BBB();
}
}
class AAA
{
{
System.out.println("AAA的普通代码块");
}
public AAA(){
//1,super()
//2.调用本类的普通代码块
System.out.println("AAA构造器被调用");}
}
class BBB extends AAA
{
{
System.out.println("BBB的普通代码块");
}
public BBB(){
//1,super()
//2.调用本类的普通代码块
System.out.println("BBB构造器被调用");}
}
/*
AAA的普通代码块
AAA构造器被调用
BBB的普通代码块
BBB构造器被调用
*/
  1. 我们看一下创建一个子类时(继承关系),他们的静态代码块,静态属性初始化,普通代码块,普通属性初始化,构造方法的调用顺序如下;

    1. 父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
    2. 子类的静态代码块和静态属性(优先级一样,按定义顺序执行)
    3. 父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
    4. 父类的构造方法
    5. 子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
    6. 子类的构造方法

    陈士虎的理解:

    当有继承关系的时候,只有子类的静态相关的在父类的前面执行,其余的都在父类的后面执行

    package D06;
    public class D05 {
    public static void main(String[] args) {
    //老师说明
    //(1) 进行类的加载,执行静态相关的东西
    //1.1 先加载 父类 A02 1.2 再加载 B02
    //(2) 创建对象,执行构造器
    //2.1 从子类的构造器开始
    new B02();//对象
    // new C02();
    }
    }
    class A02 { //父类
    private static int n1 = getVal01();
    static {
    System.out.println("A02 的一个静态代码块..");//
    }
    {
    System.out.println("A02 的第一个普通代码块..");//
    }
    public int n3 = getVal02();//普通属性的初始化
    public static int getVal01() {
    System.out.println("getVal01的静态属性");//
    return 10;
    }
    public int getVal02() {
    System.out.println("getVal02的普通属性");//
    return 10;
    }
    public A02() {//构造器
    //隐藏
    //super()
    //普通代码和普通属性的初始化......
    System.out.println("A02 的构造器");//
    }
    }
    class C02 {
    private int n1 = 100;
    private static int n2 = 200;
    private void m1() {
    }
    private static void m2() {
    }
    static {
    //静态代码块,只能调用静态成员
    //System.out.println(n1);错误
    System.out.println(n2);//ok
    //m1();//错误
    m2();
    }
    {
    //普通代码块,可以使用任意成员
    System.out.println(n1);
    System.out.println(n2);//ok
    m1();
    m2();
    }
    }
    class B02 extends A02 { //
    private static int n3 = getVal03();
    static {
    System.out.println("B02 的一个静态代码块..");//
    }
    public int n5 = getVal04();
    {
    System.out.println("B02 的第一个普通代码块..");//
    }
    public static int getVal03() {
    System.out.println("getVal03的静态属性");//
    return 10;
    }
    public int getVal04() {
    System.out.println("getVal04的普通属性");//
    return 10;
    }
    //一定要慢慢的去品..
    public B02() {//构造器
    //隐藏了
    //super()
    //普通代码块和普通属性的初始化...
    System.out.println("B02 的构造器");//
    // TODO Auto-generated constructor stub
    }
    }
    /*getVal01的静态属性
    A02 的一个静态代码块..
    */
    /*
    getVal01的静态属性
    A02 的一个静态代码块..
    getVal03的静态属性
    B02 的一个静态代码块..
    A02 的第一个普通代码块..
    getVal02的普通属性
    A02 的构造器
    getVal04的普通属性
    B02 的第一个普通代码块..
    B02 的构造器
    */
    
  2. 静态代码块只能直接调用静态成员(静态属性和静态方法),普通代码块可以调用任意成员

public class D05 {
public static void main(String[] args) {
new C02();
}
}
class C02 {
private int n1 = 100;
private static int n2 = 200;
private void m1() {
System.out.println("不是静态的方法m1");
}
private static void m2() {
System.out.println("静态的方法m2");
}
static {
//静态代码块,只能调用静态成员
//System.out.println(n1);错误
System.out.println(n2);//ok
//m1();//错误
m2();
}
{
//普通代码块,可以使用任意成员
System.out.println(n1);
System.out.println(n2);//ok
m1();
m2();
}
}
/*
200
静态的方法m2
100
200
不是静态的方法m1
静态的方法m2
*/

课堂练习

题一

下面的代码输出什么?

package D06;
public class Do6 {
public static void main(String[] args) {
System.out.println("total="+person.total);
System.out.println("total="+person.total);
}
}
class person
{
public static int total;//第一个执行的,但是没有打印出来
static
{
total=100;
System.out.println("in static block!");
}
}
/*
in static block!
//只执行一次
total=100
total=100
*/

题二

package D06;
public class D07 {
public static void main(String[] args) {
Test test = new Test();
}
}
class Sample
{
Sample(String s)
{
System.out.println(s);
}
Sample()
{
System.out.println("Sample默认构造函数被调用");
}
}
class Test
{
Sample sam1=new Sample("sam1成员初始化");
static Sample sam=new Sample("静态成员sam初始化");
static {
System.out.println("static块执行");
}
Test()
{
System.out.println("Test默认构造函数被调用");
}
}
/*
静态成员sam初始化
static块执行
sam1成员初始化
Test默认构造函数被调用
*
* */

抽象类

为什么要用抽象类

老是在想为什么要引用抽象类,一般类不就够用了吗。一般类里定义的方法,子类也可以覆盖,没必要定义成抽象的啊。

其实不是说抽象类有什么用,一般类确实也能满足应用,但是现实中确实有些父类中的方法确实没有必要写,因为各个子类中的这个方法肯定会有不同,所以没有必要再父类里写。当然你也可以把抽象类都写成非抽象类,但是这样没有必要。

而写成抽象类,这样别人看到你的代码,或你看到别人的代码,你就会注意抽象方法,而知道这个方法是在子类中实现的,所以,有个提示作用。

注意

  • abstract修饰符可以用来修饰方法也可以修饰类,如果修饰方法,那么该方法就是抽象方法;如果修饰类,那么该类就是抽象类
  • 抽象类中可以没有抽象方法,但是又抽象方法的类一定要声名为抽象类
  • 抽象类,不能使用new关键字来创建对象,它是用来让子类继承的
  • 抽象方法,只有方法的声明,没有方法的实现,它是用来让子类实现的
  • 子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类
  • 如果子类只覆盖了部分抽象方法,那么该子类还是一个抽象类。
  • 由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。
  • 抽象类:单继承

特点

  1. 抽象方法一定在抽象类中。

  2. 抽象方法和抽象类都必须被abstract关键字修饰。

  3. 抽象类不可以用new创建和实例化对象。因为抽象类本身就是不完整的。

  4. 抽象类中的抽象方法要被使用,必须由子类复写所有的抽象方法后,建立子类对象调用。

  5. 抽象方法的修饰符不可以是private,因为这样的话,无法被子类继承

  6. 注意点: 如果子类只覆盖了部分抽象方法,那么该子类还是一个抽象类。

抽象类和普通类的区别

抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。

abstract 关键字,和哪些关键字不能共存。

  • final:被final修饰的类不能有子类(不能被继承)。而被abstract修饰的类一定是一个父类(一定要被继承)。
  • private: 抽象类中的私有的抽象方法,不被子类所知,就无法被复写。 而抽象方法出现的就是需要被复写。
  • static:如果static可以修饰抽象方法,那么连对象都省了,直接类名调用就可以了。 可是抽象方法运行没意义。

接口

  • 普通类:只有具体实现
  • 抽象类:具体实现和规范(抽象方法)都有
  • 接口:只有规范!自己无法写方法,专业的约束!约束和实现分离:面向接口编程~

本质

  • 接口就是规范,定义的是一组规则,体现了显示世界中“如果你是。。则必须能。。”的思想。如果你是天使,则必须能飞,如果你是汽车,则必须能跑,如果你是好人,则必须干掉坏人,如果你是坏人,则必须欺负好人;

  • 接口的本质是锲约,就像我们人间法律一样,制定好后大家都遵守

特点

public interface A
{
void run();
}
//接口里面的方法的修饰词默认的就是public abstract,且你写了其他的也没用,必须就是public abstract;
  • 接口定义的关键字 interface。接口都需要有实现类
  • 类 可以实现接口通过关键字implements 接口,这个类就要重写这个接口里面的所有方法
public class B implements A
{
@override
public void run()
{
}
}
  • 利用接口实现多继承
//接口A
public interface A
{
void run();
}
//接口B
public interface B
{
void go();
}
//C类实现接口A和接口B
public class C implements A,B
{
@override
public void run()
{
}
@override
public void go()
{
}
}

作用

  • 接口可以用来约束
  • 定义一些方法让不同的人来实现
  • 方法都是public abstract的
  • 属性都是public static final的
  • 接口不能被实例化,接口中没有构造方法
  • implements可以实现多个接口
  • 实现接口必须要重写接口里面的方法

枚举和注解

自定义类实现枚举

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i9FGX0Br-1640005795989)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211203085725495.png)]

问题:

创建Season对象有如下特点

  • 季节的值是有限的几个值(spring ,sunmmer,autumn,winter)
  • 只读,不需要修改

解决方案:

  • 枚举对应英文(enumeration,简写enum)
  • 枚举是一组常量的集合
  • 可以这里理解:枚举属于一种特殊的类,里面只包含一组有限的特定对象
package D02;
public class D01
{
public static void main(String[] args) {
System.out.println(Season.AUTNEN);
}
}
//演示定义枚举实现
class Season
{
private String name;
private String desc;
//定义了四个对象
public final static Season SPRING =new Season("春天","温暖");
public final static Season SUMMER =new Season("夏天","温暖");
public final static Season AUTNEN =new Season("秋天","温暖");
public final static Season WINTER =new Season("冬天","温暖");
/*1 将构造器私有化,目的防止,直接 new
2
去掉setXxx方法,防止属性被修改
3
在Season 内部,直接创建固定的对象
4
优化,可以加入final修饰符
* */
private Season(String name, String desc) {
this.name = name;
this.desc = desc;//描述
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
//必须要toString 不要会输出地址
@Override
public String toString() {
return "Season{" +
"name='" + name + ''' +
", desc='" + desc + ''' +
'}';
}
}
//Season{name='秋天', desc='温暖'}

小结:进行自定义类实现枚举,有如下特点:

  1. 构造器私有化
  2. 本类内部创建一组对象
  3. 对外暴露对象(通过为对象public final static 修饰符)
  4. 可以提供get()方法,但是不要提供set方法

enum关键字实现枚举

package D02;
public class D02 {
public static void main(String[] args) {
System.out.println(Season1.SUMMER);
//这里可以直接的点出来类名.对象
}
}
//演示使用enum关键字是实现枚举类
enum Season1
{
//定义了四个对象
//
public final static Season SPRING =new Season("春天","温暖");
//
public final static Season SUMMER =new Season("夏天","温暖");
//
public final static Season AUTNEN =new Season("秋天","温暖");
//
public final static Season WINTER =new Season("冬天","温暖");
//如果使用了enum来实现枚举类
//1.使用关键字enum替代class
//2. public final static Season SUMMER =new Season("夏天","温暖");用
//这个替代SUMMER("春天","温暖");
//3 .如果有多个常量(对象),使用“,”隔开
//SUMMER("春天","温暖"),WINTER("冬天","很冷");
//4.如果使用enum来实现枚举,要求将定义常量对象,写在最前面
SUMMER("春天","温暖"),WINTER("冬天","很冷"),fds;//无参的构造器,()可以省略
private String name;
private String desc;
private Season1(String name, String desc) {
this.name = name;
this.desc = desc;//描述
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
@Override
public String toString() {
return "Season{" +
"name='" + name + ''' +
", desc='" + desc + ''' +
'}';
}
}
//Season{name='春天', desc='温暖'}

hello.java-------->Javac编译----------------->hello.class

hello.class-------->Javap反编译----------------->hello.java

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OmAGCcyN-1640005795990)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211203102923552.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n1myJb0M-1640005795991)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211203103042923.png)]

JDK内置的基本注解类型

元注解:对注解进行注解

异常机制

什么是异常

  • 实际工作中,遇到的情况不可能是非常完美的。比如:你写的某个模块,用户输入不一定符合你的要求、你的程序要打开某个文件,这个文件可能不存在或者文件格式不对,你要读取数据库的数据,数据可能是空的等。我们的程序再跑着,内存或硬盘可能满了。等等。
  • 软件程序在运行过程中,非常可能遇到刚刚提到的这些异常问题,我们叫异常,英文是:Exception,意思是例外。这些,例外情况,或者叫异常,怎么让我们写的程序做出合理的处理。而不至于程序崩溃。
  • 异常指程序运行中出现的不期而至的各种状况,如:文件找不到、网络连接失败、非法参数等。异常发生在程序运行期间,它影响了正常的程序执行流程。

异常体系结构

  • 要理解Java异常处理是如何工作的,你需要掌握以下三种类型的异常:

    1. 检查性异常:最具代的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。
      例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。

    2. 运行时异常:运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在
      编译时被忽略。

    3. 错误(error):错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢
      出时,一个错误就发生了,它们在编译也检查不到的。

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sfIbsNL5-1640005795992)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211129082132404.png)]

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UkGRbaJb-1640005795992)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211129082142715.png)]

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fKlsZRz1-1640005795993)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211129082150424.png)]

java异常处理机制

抛出异常

if(b==0)
{
//主动抛出异常,用的是throw;一般在方法中使用;
thorw new ArithmeticException();
}
//假设这方法中,处理不了这个异常,方法上抛出异常,,用throws
public void test (int a) throws ArithmeticException
{
if(b==0)
{
}
}

捕获异常

异常处理的五个关键字

  • try

  • catch

  • finally

  • throw

  • throws

    快捷键:Ctrl+Alt+T

处理异常

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eQnF3lsw-1640005795994)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211129082104711.png)]

try 和catch必须要的

自定义异常

总结

  • 处理运行时异常时,采用逻辑去合理规避同时辅助try-catch处理
  • 在多重catch块后面,可以加一个catch(Exception)来处理可能会被遗漏的异常
  • 对于不确定的代码,也可以加上try-catch。处理潜在的异常
  • 尽量去处理异常,切记只是简单的调用printStackTrace()去打印输出
  • 具体如何处理异常,要根据不同的业务需求和异常类型去决定
  • 尽量添加finally语句块去释放占用的资源

#集合

集合基础

集合类特点:提供一个存储空间可变的存储模型,存储的数据容量可以发生改变

1.1ArrayList集合

ArrayList:

  • 可调整大小的数组实现
  • :是一种特殊的数据类型,泛型

怎么用呢?

  • 在出现E的地方我们使用引用数据类型替换即可

    举例:ArrayList , ArrayList

1.2ArrayList构造方法和添加方法

方法名说明
public ArrayList()创建一个空的集合对象
public boolean add(E e)将指定的元素追加到此集合的末尾
public void add(int index,E element)在此集合中的指定位置插入指定的元素

index:是数组里面的下标 如果下标只有0.1.2的话,你填4的话就会报错;

element:是添加的元素内容

创建一个新的集合:

ArrayList<String>
array= new ArrayList<String> ();
//或者
ArrayList<String>
array= new ArrayList<> ();
//后面的String可以不写,可以根据前面的那个推断出来

例子

public
class xjao
{
public static void main(String[] args) {
ArrayList<String> array=new ArrayList<String>();
array.add("as");
array.add("chenshihu");
array.add(2,"sdfds");
System.out.println(array);
}
}
//打印结果
//[as, chenshihu, sdfds]

1.3ArrayList集合常用方法

方法名说明
public boolean remove(Object o)删除指定的元素,返回删除是否成功
public E remove(int index)删除指定索引处的元素,返回被删除的元素
public E set(int index,E element)修改指定索引处的元素,返回被修改的元素
public E get(int index)返回指定索引处的元素
public int size()返回集合中的元素的个数

E 是数据类型

案例:存储字符串并遍历

public
class xjao
{
public static void main(String[] args)
{
ArrayList<String> array=new ArrayList<String>();
array.add("as");
array.add("chenshihu");
array.add(2,"sdfds");
for(int i=0;i<array.size();i++)
{
System.out.println(array.get(i));
}
}
}
打印结果:as
chenshihu
sdfds

案例:存储学生对象并遍历

//学生类
public class Student
{
private int age;
private String name;
public Student(int age,String name)
{
this.age=age;
this.name=name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public
class xjao
{
public static void main(String[] args) {
ArrayList<Student> array=new ArrayList<Student>();
Student s1=new Student(23, "陈士虎");
Student s2=new Student(21,"陶雨");
array.add(s1);
array.add(s2);
for(int i=0;i<array.size();i++)
{
Student s=array.get(i);
System.out.println(s.getName()+","+s.getAge());
}
}
}
//打印结果
//陈士虎,23
//陶雨,21

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0WaJfLAb-1640005795995)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211129082028546.png)]

集合进阶

Collection

1. 集合类体系结构
2. Collection集合概述和使用
  • 是单列集合的顶层接口,它表达一组对象,这些对象也称为Collection的元素
  • JDK不提供此接口的任何直接实现,它提供更具体的子接口(如Set和List)实现

创建Collection集合的对象

  • 多态的方式
  • 具体的实现类ArrayList
public
class xjao
{
public static void main(String[] args) {
Collection <String> array=new ArrayList<String>();
array.add("as");
array.add("chenshihu");
array.add("sdfds");
System.out.println(array);
}
}
//打印结果:[as,chenshihu,sdfds]
3. Collection集合常用方法
方法名说明
boolean add(E e)添加元素
boolean remove(Object o)从集合中移除指定的元素
void clear()清空集合中的元素
boolean contains(Object o)判断集合中是否存在指定的元素
boolean isEmpty()判断集合是否为空
int siza()集合的长度,也就是集合中元素的个数
4. Collection集合的遍历

lterator:迭代器,集合专用遍历方式

  • lteratoriterator();返回此集合中元素的迭代器,通过集合的iterator()方法得到
  • 迭代器是通过集合的iterator()方法得到的,所以我们说它是依赖于集合而存在的

lterator中的常用方法

  • E next();返回迭代中的下一个元素

  • boolean hasNext();如果迭代具有更多元素,则返回true

    public class day01 {
    public static void main(String[] args) {
    Collection<String> s = new ArrayList<>();
    s.add("xiao");
    s.add("shun");
    Iterator<String> s3= s.iterator();
    System.out.println(s3.next());
    System.out.println(s3.next());//一个这个方法只打印一个元素,
    }
    }
    //打印结果
    //xiao
    //shun
    

    如果有集合里面只有三个元素,而你 System.out.println(s3.next()); 重复四次这样的操作,就会报错,报没有找不到第四个下标的异常,boolean hasNext();这个方法就可以解决这个问题

    public class day01 {
    public static void main(String[] args) {
    Collection<String> s = new ArrayList<>();
    s.add("xiao");
    s.add("shun");
    Iterator<String> s3= s.iterator();
    if(s3.hasNext()) //如果集合里面有元素的话,为真,
    {
    System.out.println(s3.next());
    }
    if(s3.hasNext()) //这个写多了也没事的,没有元素就不会打印
    {
    System.out.println(s3.next());
    }
    }
    }
    //打印结果
    //xiao
    //shun
    

    可以while循环解决这写繁琐的操作

    public class day01 {
    public static void main(String[] args) {
    Collection<String> s = new ArrayList<>();
    s.add("xiao");
    s.add("shun");
    Iterator<String> s3= s.iterator();
    while (s3.hasNext()) //如果集合里面有元素的话,为真,
    {
    String s4=s3.next();//并不是每次只是输出s3.next()
    System.out.println(s4);
    }
    }
    }
    //打印结果
    //xiao
    //shun集合的使用步骤
    
5集合的使用步骤

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XxbYKUMH-1640005795996)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211129082007657.png)]

案例:Collection集合存储学生对象并遍历

思路:

  1. 定义学术类
  2. 创建Collection集合对象
  3. 创建学生对象
  4. 把学生添加到集合
  5. 遍历集合(迭代器方式)
public class day01 {
public static void main(String[] args) {
Collection<Student> s = new ArrayList<>();
Student s1 = new Student(20, "陶雨");
Student s2 = new Student(22, "陈士虎");
s.add(s1);
s.add(s2);
Iterator<Student> s3=s.iterator();
while (s3.hasNext())
{
Student s5=s3.next();
System.out.println(s5.getAge()+"+"+s5.getName());
}
}
}
/*打印结果
20+陶雨
22+陈士虎
*/

List

1. List集合概述和特点

List集合概述

  • 有序集合(也称序列),用户可以精确控制列表中每个元素的插入位置,用户可以通过整数索引访问元素,并搜索列表中的元素。
  • 与Set集合不同,列表通常允许重复的元素

List集合特点

  • 有序:存储和取出的元素顺序一致
  • 可重复:存储的元素可以重复
public class day01 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("陈士虎");
list.add("陶雨");
list.add("陈士虎");
Iterator<String> s=list.listIterator();
while (s.hasNext())
{
System.out.println(s.next());
}
}
}
/*打印结果
陈士虎
陶雨
陈士虎
*/
2. List集合特有的方法
方法名说明
void add(int index ,E element)在此集合中的指定位置插入指定的元素
E remove(int index)删除指定索引处的元素,返回被删除的元素
E set(int index,E element)修改指定索引处的元素,返回被修改的元素
E get(int index)返回指定索引处的元素
案例:List集合存储学生对象并遍历

思路:

  1. 定义学术类
  2. 创建List集合对象
  3. 创建学生对象
  4. 把学生添加到集合
  5. 遍历集合(迭代器方式,for循环方式)
import java.util.*;
public class day01 {
public static void main(String[] args) {
List<Student> list = new ArrayList<>();
Student student1 = new Student(20, "陈士虎");
Student student2 = new Student(22, "陶雨");
list.add(student1);
list.add(student2);
//迭代器方法
//
Iterator<Student> i = list.iterator();
//
while (i.hasNext())
//
{
//
Student s=i.next();
//
System.out.println(s.getAge()+"+"+s.getName());
//
}
//for循环方法
for(int i=0;i<list.size();i++)
{
Student s1=list.get(i);
System.out.println(s1.getAge()+"+"+s1.getName());
}
}
}
3 . 并发修改异常

遍历集合,得到每一个元素,看有没有”taoyu“这个元素,如果有,我就添加一个“chenshihu”元素,

import java.util.*;
public class day01 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("taoyu");
list.add("chenshihu");
for(int i=0;i<list.size();i++)
{
String s1=list.get(i);
if(s1.equals("taoyu")){
list.add("chenshihu");
}
}
System.out.println(list);
}
}
  • 并发修改异常:ConcurrentModificationException
  • 产生原因:迭代器遍历的过程中,通过集合对象修改了集合元素的长度,造成了迭代器获取元素中判断预期修改值和实际修改值不一致
  • 解决方案:用for循环遍历,然后用集合对象做对应的操作即可
4. ListIterator

ListIterator:列表迭代器

  • 通过List集合的listIterator()方法得到的,所以说它是List特有的迭代器
  • 用于允许程序员沿任一方向遍历列表的列表迭代器,在迭代期间修改列表,并获取列表中迭代器的当前位置

ListIterator中常用的方法

方法名说明
E next()返回迭代中的下一个元素
boolean hasNext()如果迭代具有更多元素,则返回true
E previous()返回列表中的上一个元素
boolean hasPrevious()如果此列表迭代器在相反方向遍历列表时具有更多元素,则返回true
void add(E e)将指定的元素插入列表
import java.util.*;
public class day01 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("taoyu");
list.add("chenshihu");
ListIterator<String> l=list.listIterator();
while (l.hasNext())
{
String s=l.next();
if(s.equals("taoyu"))
{
l.add("chenshihu");//这样不会报错,这是 ListIterator<特有的。
}
}
System.out.println(list);
}
}
5。增强for循环

增强for:简化数据和collection集合的遍历

  • 实现Iterable接口的类允许其对象成为增强型for语句的目标
  • 它是JDK5之后出现的,其内部原理时一个Iterator迭代器

增强for的格式

  • 格式:

    for(元素数据类型 变量名:数组或者Collection集合)

    {

    ​ 在此处使用变量即可,该变量就是元素

    }

  • 范例:

    int[] arr={1,2,3,4,5}

    for(int i:arr)

    {

    System.out.println(i)

    }

import java.util.*;
public class day01 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("taoyu");
list.add("chenshihu");
//内部原理是一个Iterator迭代器
//增加一个元素也会报错ConcurrentModificationException
for(String s:list)
{
System.out.println(s);
}
}
}
/*
打印结果
taoyu
chenshihu
*/

案例:List集合存储学生对象用三种方式遍历

需求:创一个存储学生对象的集合,存储3个学生对象,使用程序实现在控制台遍历该集合

思路:

  1. 定义学生类

  2. 创建list集合对象

  3. 创建学生对象

  4. 把学生添加到集合

  5. 遍历集合

    • 迭代器:集合特有的遍历方式

    • 普通for:带有索引的遍历方式

    • 增强for:最方便的遍历方式


ArrayList<Student> a = new ArrayList<>();
Student s1 = new Student(20, "chenshihu");
Student s2 = new Student(22, "taoyu");
a.add(s2);
a.add(s1);
//
for(Student t:a)
{
System.out.println(t.getAge()+","+t.getName());
}
}
6数据结构

数据结构是计算机存储、组织数据的方式。是指相互之间存在一种或多种特定关系的数据元素的集合

通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率

常见数据结构:

  • 栈是一种数据先进后出的模型
队列
  • 队列是一种先进先出的模型哦
数组

特点:

  • 查询数据通过索引定位,查询任意数据耗时相同,查询效率高
  • 删除数据时,要将原始数据删除,同时后面每个数据前移,删除效率低
  • 添加数据时,添加位置后的每个数据后移,在添加元素,添加效率极低
链表

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PoVZOCRV-1640005795997)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211129081942461.png)]

  • 链表是一种增删快的模型(对比数组)

  • 查询慢(对比数组)(查询必须从头节点开始查询)

7 List集合子类特点

list集合常用子类:Arraylist、LinkedList

  • ArrayList:底层数据结构是数组,查询快,增删慢
  • LinkedList:底层数据结构是链表,查询慢,增删快

8LinkedList集合的特有功能

方法名说明
public void addFirst(E e)在该列表开头插入指定的元素
public void addLast(E e)将指定的元素追加到此列表的末尾
public E getFirst()返回此列表中的第一个元素
public E getLast()返回此列表中的最后一个元素
public E removeFirstt()从此列表中删除并返回第一个元素
public E removeLast()从此列表中删除并返回最后一个元素

Set

1. Set集合概述和特点

Set集合特点

  • 不包含重复元素的集合
  • 没有带索引的方法,所以不能使用普通for循环遍历

Set集合练习

  • 存储字符串并遍历
public class sa {
public static void main(String[] args) {
Set<String> a = new HashSet<>();
a.add("陶雨");
a.add("陈士虎");
a.add("游戏");
a.add("陶雨");//不包含重复元素
for (String b : a)
{
System.out.println(b);
}
}
}
/*
打印结果
陶雨
陈士虎
游戏
*/
2. 哈希值

哈希值:是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值

Object类中有一个方法可以获得对象的哈希值

  • public int hashCode():返回对象的哈希码值

对象的哈希值特点

  • 同一个对象多次调用hashCode()方法返回的哈希值是相同的

  • 默认情况下,不同对象的哈希值是不同的,而重写hashCode()方法,可以实现让不同对象的哈希值相同

public class sa {
public static void main(String[] args)
{
Student s1=new Student(21,"陈士虎");
System.out.println(s1.hashCode());
System.out.println(s1.hashCode());
System.out.println("----------------------");
Student s2=new Student(32,"陶雨");
System.out.println(s2.hashCode());
System.out.println("----------------------");
System.out.println("Hello".hashCode());
System.out.println("world".hashCode());
System.out.println("java".hashCode());
System.out.println("world".hashCode());
System.out.println("----------------------");
System.out.println("鲜红".hashCode());
System.out.println("反对".hashCode());
}
}
/*
37713239
37713239
----------------------
1234643
----------------------
69609650
113318802
3254818
113318802
----------------------
1179395
1179395
*/
3. HashSet集合概述和特点

hashSet集合特点

  • 底层数据结构是哈希表
  • 对集合的迭代顺序不作任何保证,也就是说不保证存储和取出的元素顺序一致
  • 没有带索引的方法,所以不能使用普通for循环遍历
  • 由于是Set集合,所以是不包含重复元素的集合

HashSet集合练习

  • 存储字符串并遍历
import java.util.HashSet;
public class sa {
public static void main(String[] args)
{
HashSet<String> a=new HashSet<>();
a.add("陈士虎");
a.add("陶雨");
a.add("蛋糕");
a.add("蛋糕");
for(String b:a)
{
System.out.println(b);
}
}
}
/*
陶雨
顺序不保证一致
陈士虎
蛋糕
*/
4. 哈希表

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2vIrfQXr-1640005795998)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211129081924882.png)]

案例

HashSet集合存储学生对象并遍历

需求:创建一个存储学生对象的集合,存储多个学生对象,使用程序实现在控制台遍历该集合

要求:学生对象的成员变量值相同,我们就认为是同一个对象

思路:

  • 定义学生类

  • 创建HashSet集合对象

  • 创建学生对象

  • 把学生添加到集合

  • 遍历集合

  • 在学生类中重写两个方法

    ​ 1. hashCode()和equals()方法

    2. 自动生成即可
    
//学生类
import java.util.Objects;
public class Student
{
private int age;
private String name;
public Student(int age,String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(age, name);
}
}
//测试类
import java.util.HashSet;
public class sa {
public static void main(String[] args)
{
HashSet<Student> a=new HashSet<>();
Student s1 = new Student(23, "陈士虎");
Student s2 = new Student(21, "陶雨");
Student s3= new Student(21, "陶雨");
a.add(s1);
a.add(s2);
for(Student s:a)
{
System.out.println(s.getAge()+"+"+s.getName());
}
}
}
/*
23+陈士虎
21+陶雨
*/
5. LinkedHashSet集合概述和特点

LinkedHashSet集合特点

  • 哈希表和链表实现的Set接口,具有可预测的迭代次序

  • 由链表保证元素有序,也就是说元素的存储和取出顺序一致的

  • 由哈希表保证元素唯一,也就是说没有重复的元素

LinkedHashSet集合练习

存储字符串并遍历

import java.util.LinkedHashSet;
public class sa {
public static void main(String[] args)
{
LinkedHashSet<String> a=new LinkedHashSet<>();
a.add("陈士虎");
a.add("陶雨");
a.add("蛋糕");
for(String s:a)
{
System.out.println(s);
}
}
}
/*
陈士虎
陶雨
蛋糕
*/
6. TreeSet

treeSet集合特点

  • 元素有序,这里的顺序不是指存储和取出的顺序,而是按照一定的规则进行排序,具体排序方式取决于构造方法

    ​ TreeSet():根据其元素的自然排序进行排序

    ​ TreeSet(Comparator comparator):根据指定的比较器进行排序

  • 没有带索引的方法,所以不能使用普通for循环遍历

  • 由于是Set集合,所以不包含重复元素的集合

    TreeSet集合练习

  • 存储整数并遍历

import java.util.LinkedHashSet;
import java.util.TreeSet;
public class sa {
public static void main(String[] args)
{
TreeSet<Integer> a=new TreeSet<>();
a.add(23);
a.add(43);
a.add(32);
a.add(62);
a.add(23);
for(Integer e:a)
{
System.out.println(e);
}
}
}
/*
23
32
43
62
*/

自然排序Comparable的使用

  • 存储学生对象并遍历,创建TreeSet集合使用无参构造方法
  • 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序

结论:

  • 用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的
  • 自然排序,就是让元素所属的类实现Comparable接口,重写compareTo(T o)方法
  • 重写方法时,一定要注意排序规则必须按照要求和主要条件和次要条件来写
//测试类
import java.util.TreeSet;
public class sa {
public static void main(String[] args)
{
TreeSet<Student> a=new TreeSet<>();
Student s1=new Student(23,"chenshihu");
Student s2=new Student(24,"chenshihu");
Student s3=new Student(25,"chenshihu");
Student s4=new Student(26,"chenshihu");
Student s5=new Student(26,"taoyu");
a.add(s1);
a.add(s2);
a.add(s3);
a.add(s4);
a.add(s5);
for(Student r:a)
{
System.out.println(r.getAge()+","+r.getName());
}
}
}
/*打印结果
26,chenshihu
26,taoyu
25,chenshihu
24,chenshihu
23,chenshihu
*/
//学生类
import java.util.Objects;
public class Student implements Comparable<Student>
{
private int age;
private String name;
public Student(int age,String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(age, name);
}
@Override
public int compareTo(Student o) {
//按年龄从大到小的排序
int a=o.age-this.age;
//年龄相同时,按照姓名的字母排序
int a1=a==0?this.name.compareTo(o.name):a;
return a1;
}
}
23,chenshihu
24,chenshihu
25,chenshihu
26,chenshihu
26,taoyu
//学生类
import java.util.Objects;
public class Student
{
private int age;
private String name;
public Student(int age,String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(age, name);
}
}
7. 比较器排序Comparator的使用
  • 存储学生对象并遍历,创建TreeSet集合使用带参构造方法
  • 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序

结论:

  • 用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序的
  • 比较器排序,就是让集合集合构造方法接收Comparator的实现类对象,重写compare(TO 1,TO 2)方法
  • 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
import java.util.*;
public class day01 {
public static void main(String[] args) {
TreeSet<Student> a=new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
int s=o1.getAge()-o2.getAge();
int w=s==0? o1.getName().compareTo(o2.getName()):s;
return w;
}
});
Student s1=new Student(23,"chenshihu");
Student s2=new Student(24,"chenshihu");
Student s3=new Student(25,"chenshihu");
Student s4=new Student(26,"chenshihu");
Student s5=new Student(26,"taoyu");
a.add(s1);
a.add(s2);
a.add(s3);
a.add(s4);
a.add(s5);
for(Student r:a)
{
System.out.println(r.getAge()+","+r.getName());
}
}
}
案例:成绩排序

需求:用TreeSet集合存储多个学生信息(姓名,语文成绩,数学成绩),并遍历该集合

要求:按照总分从高到低出现

思路:

  1. 定义学生类
  2. 创建TreeSet集合对象,通过比较器排序进行排序
  3. 创建学生对象
  4. 把学生对象添加到集合
import java.util.Comparator;
import java.util.TreeSet;
public class Dou
{
public static void main(String[] args)
{
TreeSet<St> a=new TreeSet<>(new Comparator<St>() {
@Override
public int compare(St o1, St o2) {
//主要条件
int e=(o1.getShuxue()+o1.getYuweng())-(o2.getShuxue()+o2.getYuweng());
//次要条件,可以有一个,也可以由多个;
int r=e==0?o1.getName().compareTo(o2.getName()):e;
return r;
}
});
St a1=new St("陈士虎",23,32);
St a2=new St("陶雨",24,46);
St a3=new St("小宝宝",24,46);
St a4=new St("小宝的",24,46);
a.add(a1);
a.add(a2);
a.add(a3);
a.add(a4);
for(St r:a)
{
System.out.println(r.getName()+"+"+r.getYuweng()+"+"+r.getShuxue());
}
}
}
陈士虎+23+32
小宝宝+24+46
小宝的+24+46
陶雨+24+46
案例:不重复的随机数

需求:编写一个程序,获得10个1-20之间的随机数,要求随机数不能重复,并在控制台输出

思路:

  1. 创建Set集合对象
  2. 创建随机数对象
  3. 判断集合的长度是不是小于10;是:产生一个随机数,添加到集合,回到3继续
  4. 遍历集合
import java.util.*;
public class Dou
{
public static void main(String[] args)
{
Set<Integer> a=new HashSet<>();
Random r=new Random();
while (a.size()<10)
{
int e=r.nextInt(20);
a.add(e);
}
System.out.println(a);
for(Integer t:a) {
System.out.print(t+",");
}
}
}
/*
打印结果:
[16, 0, 2, 19, 6, 7, 8, 10, 11, 14]
16,0,2,19,6,7,8,10,11,14,
*/

泛型

1. 概述

泛型定义格式:

  • <类型>:指定一种类型的格式。这里的类型可以看成是形参
  • <类型1,类型2…>:指定多种类型的格式,多种类型之间用逗号隔开,这里的类型可以看成是形参
  • 将来具体调用时侯给定的类型可以看成是实参,并且实参的类型只能是引用数据类型

这个 E 只能是引用数据类型

泛型的好处:

  • 把运行时期的问题提前到了编译期间
  • 避免了强制类型转换
2. 泛型类

泛型类的定义格式:

  • 格式:修饰符class类名<类型>()

  • 范例:public class Generic()

    此处可以随便写为任意标识,常见的如T,E,K,V等形式的参数常用于表示泛型

    细节:

    1. 泛型类可以定义多个参数类型
    2. 泛型类的构造器不可以定义泛型
    3. 不同的泛型对的引用类型不可以相互赋值
    4. 泛型如果不指定,那么就会被擦除,泛型对应的类型为ObJect类型
    5. 泛型类中的静态方法不能实用类的泛型
    6. 不能直接使用E[]的创建
//Guil类
public class Gui1<T>
{
private T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
//测试类
public class test {
public static void main(String[] args) {
Gui1<String> a=new Gui1<>();
a.setT("sd");
System.out.println(a.getT());
Gui1<Integer> b=new Gui1<>();
b.setT(32);
System.out.println(b.getT());
}
}
//打印结果
//sd
//32
3. 泛型方法

泛型方法的定义格式:

  • 格式:修饰符 <类型> 返回值类型 方法名(类型 变量名)
  • 范例:public void show(T t)()

什么是泛型方法

  • 不是带泛型的方法就是泛型方法

  • 泛型方法有要求:这个方法的泛型的参数类型要和当前的类的泛型无关

    换个角度:

    泛型方法对应的那个泛型参数类型和当前所在的类 是否是泛型类,如是 泛型类的泛型是什么都无关

  • 泛型方法定义的时候,前面要加上

    原因:如果不加的话,会把T当作一种数据类型,然而代码中没有T类型那么就会报错

  • T的类型是在调用方法的时候确定

  • 泛型方法可否是静态方法?可以是静态方法

    pubic static void a(E e)//不可以为静态方法
    
import java.util.ArrayList;
import java.util.List;
public class test {
public static void main(String[] args)
{
Object a1=new Object();
String s=new String();
a1=s;//多态的一种形式
Object[] b1=new Object[10];
String[] s1=new String[10];
b1=s1;//多态的一种形式
List<Object> list1=new ArrayList();
List<String> list2=new ArrayList();
list1=list2;
//A和B是子类父类的关系,但是 List<Object>和 List<String>不存在继承关系,是并列关系
}
}
//Guil类
public class Gui1<T>
{
public<T> void getT(T t)
{
System.out.println(t);
}
}
//测试类
public class test {
public static void main(String[] args) {
Gui1 a=new Gui1();
a.getT("fsd");
a.getT(12);
a.getT("陈是虎");
}
}
/*打印结果:
fsd
12
陈是虎
*/
4. 泛型接口

泛型接口的定义格式:

  • 格式:修饰符interface 接口名<类型>{}
  • 范例:public interface Generic{}
//接口
public interface Gui1<T>
{
void fly(T t);
}
//实现类
public class sa<T> implements Gui1<T>{
@Override
public void fly(T t) {
System.out.println(t);
}
}
//测试类
public class test {
public static void main(String[] args) {
Gui1<String> a= new sa<String>();
a.fly("陶雨");
Gui1<Integer> b= new sa<Integer>();
b.fly(32);
}
}
/*打印结果:
陶雨
32
*/
5. 类型通配符

为了表示各种泛型List的父类,可以使用类型通配符

  • 类型通配符:<?>
  • List<?>:表示元素类型未知的List,它的元素可以匹配任何的类型
  • 这种带通配符的List仅表示它是各种泛型List的父类,并不能把元素添加到其中

如果说我们不希望List<?>是任何泛型的List的父类,只希望他代表某一类泛型List的父类,可以使用类型通配符的上限

  • 类型通配符上限:<? extends 类型>
  • List<? extends Number>:他表示的类型是Number或者其子类型

除了可以指定类型通配符的上限,我们也可以指定类型通配符的下限

  • 类型通配符下限:<? super>
  • List<? super Number>:他表示的类型是Number或者其父类型
6. 可变参数

可变参数又称参数个数可变,用作方法的形参出现,那么方法参数个数就是可变的了

  • 格式:修饰符 返回值类型 方法名(数据类型…变量名)(此处是三个点)
  • 范例:public static int sum(int…a)
public class test {
public static void main(String[] args)
{
System.out.println(get(12,21,32,21));
System.out.println(get(12,21,32,21,32));
System.out.println(get(32));
}
//如果有两个参数的,a只能放后面
public static int get(int c,int... a) {
int sum = 0;
for (int i : a) {
sum += i;
}
return sum;
}
}
/*
打印结果:
74
106
0
*/

Map

1. Map集合概述和使用

map集合概述

  • interface Map<K,V> K:键的类型 ,V:值的类型
  • 将键映射到值的对象,不能包含重复的键,每个键可以映射到最多一个值

创建Map集合的对象

  • 多态的方式
  • 具体的实现类HashMap
2. Map集合的基本功能
public class test {
public static void main(String[] args)
{
Map<String,String> a=new HashMap<>();
a.put("12","chen");
System.out.println(a);
}
}
//打印结果
//{12=chen}
方法名说明
V put(K key,V value)添加元素
V remove(Object key)根据键删除键值对元素
void clear()移除所有的键值对元素
boolean containsKey(Object key)判断集合是否包含指定的键
boolean containsValue(Object value)判断集合是否包含指定的值
boolean isEmpty()判断集合是否为空
int size()集合的长度,也就是集合中键值对的个数
3. map集合的获取功能
方法名说明
V get(Object key)根据键获值
SetkeySet()获取所有键的集合
Collectionvalues()获取所有值的集合
Set<Map.Entry<K,V>>entrySet()获取所有键值对对象的集合
public class test {
public static void main(String[] args)
{
Map<String,String> a=new HashMap<>();
a.put("12","chen");
a.put("13","taoyu");
Set<String> key=a.keySet();
for (String k:key)
{
System.out.println(k);
}
}
}
/*
12
13
*/
4. map集合的遍历(方式一)

我们刚才存储的元素都是成对出现的,所以我们把Map看成是一对夫妻的集合

遍历思路

  • 把所有的丈夫给集中起来
  • 遍历丈夫的集合,获取到每一个丈夫
  • 根据丈夫去找对应的妻子

转换为Map集合中的操作:

  • 获取所有键的集合,用keySet()方法实现
  • 遍历键的集合,获取到每一个键,用增强佛如实现
  • 根据键去找值,用get(Object key)方法实现
Map<String,String> a=new HashMap<>();
a.put("12","chen");
a.put("13","taoyu");
Set<String> key=a.keySet();
for (String k:key)
{
String value=a.get(k);
System.out.println(k+"+"+value);
}
}
/*打印结果
12+chen
13+taoyu
*/
5. map集合的遍历(方式二)

我们刚才存储的元素都是成对出现的,所以我们把Map看成是一个夫妻对的集合

遍历思路:

  • 获取所有结婚证的集合
  • 遍历结婚证的集合,得到每一个结婚证
  • 根据结婚证获取丈夫和妻子

转换为Map集合中操作

  • 获取所有键值对对象的集合

    Set<Map.Entry<K,V>>entrySet():获取所有键值对对象的集合

  • 遍历键值对对象的集合,得到每一个键值对对象

    用增强for实现,得到每一个Map.Entry

  • 根据键值对对象获取键和值

    用getKey()得到键

    用getValue()得到值

 public static void main(String[] args)
{
Map<String,String> a=new HashMap<>();
a.put("12","chen");
a.put("13","taoyu");
Set<Map.Entry<String,String>> b=a.entrySet();
for (Map.Entry<String, String> k:b)
{
String c=k.getKey();
String r=k.getValue();
System.out.println(c+"+"+r);
}
}
/*打印结果
12+chen
13+taoyu
*/
案例(一)

创建一个HashMap集合,键是学号(String),值是学生对象(Student)。存储三个键值对元素,并遍历

思路:

  • 定义学生类

  • 创建HashMap集合对象

  • 创建学生对象

  • 把学生添加到集合

  • 遍历集合

    方式1:键找值

    方式2:键值对对象找键和值

 public static void main(String[] args)
{
HashMap<String ,Student> a = new HashMap<>();
Student s1=
new Student(23,"陈士虎");
Student s2=
new Student(21,"陶雨");
a.put("游戏",s1);
a.put("吃蛋糕",s2);
Set<String> b=a.keySet();
for(String c:b)
{
Student d=a.get(c);
System.out.println(c+"+"+d.getName()+"+"+d.getAge());
}
}
}
/*打印结果
吃蛋糕+陶雨+21
游戏+陈士虎+23
*/
案例(二)

需求:创建一个HashMap集合,键值学生对象(Student),值是居住地(String),存储多个键值对元素,并遍历

要求保证键的唯一性:如果学生对象的成员变量值相同,我们就认为是同一个对象

思路:

  1. 定义学生类

  2. 创建HashMap集合对象

  3. 创建学生对象

  4. 把学生添加到集合

  5. 遍历集合

  6. 在学生类中重写两个方法

    ​ hashCode()

    ​ equals()

import com.sun.deploy.util.SyncAccess;
import java.util.*;
public class test {
public static void main(String[] args)
{
HashMap<Student,String> a=new HashMap<>();
Student s1=new Student(23,"陈士虎");
Student s2=new Student(21,"陶雨");
a.put(s1,"宿迁");
a.put(s2,"南京");
Set<Student> c=a.keySet();
for(Student s:c)
{
String d=a.get(s);
System.out.println(d+","+s.getAge()+","+s.getName());
}
}
}
/*打印结果
宿迁,23,陈士虎
南京,21,陶雨
*/
案例(三)

ArrayList集合存储HashMap元素并遍历

需求:创建一个ArrayList集合,存储三个元素,每一个元素都是HashMap,每一个HashMap的键和值都是String,并遍历

思路:

  • 创建ArrayList集合
  • 创建HasjMap集合,并添加键值对元素
  • 把HashMap作为元素添加到ArrayList集合
  • 遍历ArrayList集合
import com.sun.deploy.util.SyncAccess;
import java.util.*;
public class test {
public static void main(String[] args)
{
ArrayList<HashMap<String,String>> b=new ArrayList<>();
HashMap<String,String> a=new HashMap<>();
a.put("21","陶雨");
a.put("23","陈士虎");
HashMap<String,String> a1=new HashMap<>();
a1.put("21","陶雨");
a1.put("23","陈士虎");
HashMap<String,String> a2=new HashMap<>();
a2.put("21","陶雨");
a2.put("23","陈士虎");
b.add(a);
b.add(a1);
b.add(a2);
for(HashMap<String,String> c:b)
{
Set<String> d=c.keySet();
for(String e:d) {
String q = c.get(e);
System.out.println(q + "+" + e);
}
}
}
}
/*打印结果
陈士虎+23
陶雨+21
陈士虎+23
陶雨+21
陈士虎+23
陶雨+21
*/
案例(四)

需求:创建一个HashMap集合,存储三个键值对元素,每一个键值对元素的键都是String,值是ArrayList,

每一个ArrayList的元素是String,并遍历

思路:

  • 创建HashMap集合
  • 创建ArrayList集合,并添加元素
  • 把ArrayList作为元素添加到HashMap集合
  • 遍历HashMap集合

Collections

1.collections类的概述
  • 是针对集合操作的工具类

collections类的常用方法

  • public static <T extends Comparable<? Super T>>void sort(Listlist):将指定的列表按升序排序
  • public static void reverse(List<?>list):反转指定列表中元素的顺序
  • public static void shuffle(List<?>list):使用默认的随机源随机排列指定的列表
public static void main(String[] args)
{
List<Integer> a=new ArrayList<>();
a.add(12);
a.add(34);
a.add(46);
a.add(14);
Collections.sort(a);
System.out.println(a);
}
/*打印结果
[12, 14, 34, 46]
*/
案例(一)

ArrayList存储学生对象并排序

需求:ArrayList存储学生对象,使用Collections独对ArrayList进行排序

要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序

思路:

  1. 定义学生类
  2. 创建ArrayList集合对象
  3. 创建学生对象
  4. 把学生添加到集合
  5. 使用Collections对ArrayList集合排序
  6. 遍历集合
案例(二)

模拟斗地主

需求:通过程序实现斗地主过程中的洗牌,发牌和看牌

思路:

  1. 创建一个牌盒,也就是定义一个集合对象,用ArrayList集合是实现
  2. 往牌盒里面装牌
  3. 洗牌,也就是把牌打散,用Collections的shuffle()方法实现
  4. 发牌,也就是遍历集合,给三个玩家发牌
  5. 看牌,也就是三个玩家分别遍历自己的牌
import java.util.*;
public class test {
public static void main(String[] args)
{
//存牌
ArrayList<String> sam=new ArrayList<>();
String[] color={"♦","♠","♣","♥"};
String[] a={"2","3","4","5","6","7","8","9","10","J","Q","K","A"};
for(String b:a)
{
for(String sa:color)
{
sam.add(sa+b);
}
}
sam.add("大王");
sam.add("小王");
System.out.println(sam);
Collections.shuffle(sam);
System.out.println(sam);
//分牌
ArrayList<String> one=new ArrayList<>();
ArrayList<String> two=new ArrayList<>();
ArrayList<String> three=new ArrayList<>();
ArrayList<String> zui=new ArrayList<>();
for(int i=0;i<sam.size();i++)
{
String o=sam.get(i);
if(i>=sam.size()-3)
{
zui.add(o);
}
else if(i%3==0)
{
one.add(o);
}
else if(i%3==1)
{
two.add(o);
}
else if(i%3==2)
{
three.add(o);
}
}
System.out.println(one);
System.out.println(two);
System.out.println(three);
System.out.println(zui);
loo("陈士虎",one);
loo("陶雨",two);
loo("孩子",three);
loo("底牌",zui);
}
//看牌
public static void loo(String name,ArrayList<String> a)
{
System.out.println(name+"的牌是:");
for(String d:a) {
System.out.print(d+" ");
}
System.out.println();
}
}
/*打印结果
[♦2, ♠2, ♣2, ♥2, ♦3, ♠3, ♣3, ♥3, ♦4, ♠4, ♣4, ♥4, ♦5, ♠5, ♣5, ♥5, ♦6, ♠6, ♣6, ♥6, ♦7, ♠7, ♣7, ♥7, ♦8, ♠8, ♣8, ♥8, ♦9, ♠9, ♣9, ♥9, ♦10, ♠10, ♣10, ♥10, ♦J, ♠J, ♣J, ♥J, ♦Q, ♠Q, ♣Q, ♥Q, ♦K, ♠K, ♣K, ♥K, ♦A, ♠A, ♣A, ♥A, 大王, 小王]
[♣6, ♠2, ♥4, ♠4, ♦10, ♣10, ♠3, ♣7, ♦3, ♥7, ♥10, ♠6, ♠10, ♦5, ♥8, ♥6, ♥2, ♥3, ♦Q, 大王, ♥K, ♣8, ♦A, ♠Q, ♣4, ♦8, ♥9, ♦J, ♦9, ♦6, ♣K, ♠K, ♥A, ♣5, ♠A, ♠J, ♣J, ♠7, ♦7, ♦K, ♥J, ♠8, ♣A, ♣Q, ♦4, ♣2, ♥Q, ♠9, 小王, ♥5, ♣9, ♦2, ♣3, ♠5]
-------------------------------
[♣6, ♠4, ♠3, ♥7, ♠10, ♥6, ♦Q, ♣8, ♣4, ♦J, ♣K, ♣5, ♣J, ♦K, ♣A, ♣2, 小王]
[♠2, ♦10, ♣7, ♥10, ♦5, ♥2, 大王, ♦A, ♦8, ♦9, ♠K, ♠A, ♠7, ♥J, ♣Q, ♥Q, ♥5]
[♥4, ♣10, ♦3, ♠6, ♥8, ♥3, ♥K, ♠Q, ♥9, ♦6, ♥A, ♠J, ♦7, ♠8, ♦4, ♠9, ♣9]
[♦2, ♣3, ♠5]
-------------------------------
陈士虎的牌是:
♣6 ♠4 ♠3 ♥7 ♠10 ♥6 ♦Q ♣8 ♣4 ♦J ♣K ♣5 ♣J ♦K ♣A ♣2 小王
陶雨的牌是:
♠2 ♦10 ♣7 ♥10 ♦5 ♥2 大王 ♦A ♦8 ♦9 ♠K ♠A ♠7 ♥J ♣Q ♥Q ♥5
孩子的牌是:
♥4 ♣10 ♦3 ♠6 ♥8 ♥3 ♥K ♠Q ♥9 ♦6 ♥A ♠J ♦7 ♠8 ♦4 ♠9 ♣9
底牌的牌是:
♦2 ♣3 ♠5
*/
案例(三)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yOCsBmgF-1640005796000)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211129081846807.png)]

思路:

  1. 创建HashMap,键是编号,值是牌
  2. 创建ArrayList,存储编号
  3. 创建花色数组和点数数组
  4. 从0开始往HashMap里面存储编号,并存储对应的牌,同时往ArrayList里面存储编号
  5. 洗牌(洗的是编号),用Collections的shuffle()方法实现
  6. 发牌(发的也是编号,为了保证编号是排序的,创建TreeSet集合接收)
  7. 定义方法看牌(遍历TreeSet集合,获取编号,到HashMap集合找对应的牌)
  8. 调用看牌方法
import java.util.ArrayList;
import java.util.HashMap;
import java.util.TreeSet;
public class Dou
{
public static void main(String[] args)
{
//存牌
HashMap<Integer,String> sam=new HashMap<>();
ArrayList<Integer> sum=new ArrayList<>();
String[] color={"♦","♠","♣","♥"};
String[] a={"2","3","4","5","6","7","8","9","10","J","Q","K","A"};
int i=0;
for(String a1:a)
{
for(String co:color)
{
sam.put(i,co+a1);
sum.add(i);
i++;
}
}
sam.put(i,"大王");
sum.add(i);
i++;
sam.put(i,"小王");
sum.add(i);
i++;
System.out.println(sam);
System.out.println(sum);
System.out.println("---------------------------------------------");
//发牌
TreeSet<Integer> yi=new TreeSet<>();
TreeSet<Integer> er=new TreeSet<>();
TreeSet<Integer> san=new TreeSet<>();
TreeSet<Integer> si=new TreeSet<>();
for(int t=0;t<sum.size();t++)
{
int w=sum.get(t);
if(t>= sum.size()-3)
{
si.add(t);
}
else if(t%3==0)
{
yi.add(t);
}
else if(t%3==1)
{
er.add(t);
}
else if(t%3==2)
{
san.add(t);
}
}
System.out.println(yi);
System.out.println(er);
System.out.println(san);
System.out.println(si);
System.out.println("---------------------------------------------");
//看牌
look("陈士虎",sam,yi);
look("陶雨",sam,er);
look("孩子",sam,san);
look("底牌",sam,si);
}
public static void look(String a,HashMap<Integer,String> sam, TreeSet<Integer>
sum)
{
System.out.println(a+"的牌:");
for(Integer b:sum)
{
String c=sam.get(b);
System.out.print(c+" ");
}
System.out.println();
}
}
/*打印结果
{0=♦2, 1=♠2, 2=♣2, 3=♥2, 4=♦3, 5=♠3, 6=♣3, 7=♥3, 8=♦4, 9=♠4, 10=♣4, 11=♥4, 12=♦5, 13=♠5, 14=♣5, 15=♥5, 16=♦6, 17=♠6, 18=♣6, 19=♥6, 20=♦7, 21=♠7, 22=♣7, 23=♥7, 24=♦8, 25=♠8, 26=♣8, 27=♥8, 28=♦9, 29=♠9, 30=♣9, 31=♥9, 32=♦10, 33=♠10, 34=♣10, 35=♥10, 36=♦J, 37=♠J, 38=♣J, 39=♥J, 40=♦Q, 41=♠Q, 42=♣Q, 43=♥Q, 44=♦K, 45=♠K, 46=♣K, 47=♥K, 48=♦A, 49=♠A, 50=♣A, 51=♥A, 52=大王, 53=小王}
[0, 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]
---------------------------------------------
[0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48]
[1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 34, 37, 40, 43, 46, 49]
[2, 5, 8, 11, 14, 17, 20, 23, 26, 29, 32, 35, 38, 41, 44, 47, 50]
[51, 52, 53]
---------------------------------------------
陈士虎的牌:
♦2 ♥2 ♣3 ♠4 ♦5 ♥5 ♣6 ♠7 ♦8 ♥8 ♣9 ♠10 ♦J ♥J ♣Q ♠K ♦A
陶雨的牌:
♠2 ♦3 ♥3 ♣4 ♠5 ♦6 ♥6 ♣7 ♠8 ♦9 ♥9 ♣10 ♠J ♦Q ♥Q ♣K ♠A
孩子的牌:
♣2 ♠3 ♦4 ♥4 ♣5 ♠6 ♦7 ♥7 ♣8 ♠9 ♦10 ♥10 ♣J ♠Q ♦K ♥K ♣A
底牌的牌:
♥A 大王 小王
*/

常用类

内部类

一个java类中可以有多个class类,但是只能有一个public class

静态内部类

匿名内部类

public class outer
{}
class A
{}

局部内部类

public class Outer
{
public void A()
{
//局部内部类
class inner
{}
}
}

成员内部类

public class outer {
private int id;
piblic void out()
{
System.out.println("这是外部类的方法")}
public class Inner{
public void in()
{
System.out.println("这是内部类的方法")}
}
}
 //测试类
public static void main(String[] args)
{
Outer outer=new Outer();
//通过这个外部类来实例化内部类
Ouer.Inner inner=new Outer.Inner();
inner.in();
}

内部类可以获得外部类的私有属性

Object类

定义
  • 超类、基类,所有类的直接或者间接父类,位于继承树的最顶层

  • 任何类,如没有书写extends显示继承某个类,都默认直接继承Object类

  • Object类中所定义的方法,是所有对象都具有的方法

  • Object类型可以存储任何对象

    
    1. 作为参数,可接受任何对象
    1. 作为返回值,可返回任何对象
    
getClass()方法
//学生类
package test;
import java.util.Scanner;
public class Student
{
private int age;
private String name;
public Student(int age,String name)
{
this.age=age;
this.name=name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String toString() {
return name+","+age;
}
}
方法说明应用
public final Class getClass() { }返回引用中存储的实际对象类型通常用于判断两个引用实际存储对象类型是否一致
public
class xjao
{
public static void main(String[] args) {
Student s1=new Student(23, "陈士虎");
Student s2=new Student(21,"陶雨");
Class c1=s1.getClass();
Class c2=s2.getClass();
if(c1==c2){
System.out.println("s1和s2属于同一种类型");
}
else
{
System.out.println("s1和s2不属于同一种类型");
}
}
}
//打印结果:s1和s2属于同一种类型
hashCode()方法
  • public int hashCode(){}
  • 返回该对象的哈希码值
  • 哈希值根据对象的地址字符串数字使用hash算法计算出来的int类型的数值
  • 一般情况下相同对象返回相同哈希码
public static void main(String[] args)
{
Student s1=new Student(23, "陈士虎");
Student s2=new Student(21,"陶雨");
Class c1=s1.getClass();
Class c2=s2.getClass();
System.out.println(s1.hashCode());
System.out.println(s2.hashCode());
Student s3=s2;
//把s2的地址赋给了s3,他们的哈希值就相同
System.out.println(s3.hashCode());
}
//打印结果:
/*
366712642
1829164700
1829164700
*/
//这里面
toString()方法
  • public String toString(){}
  • 返回该对象的字符串表示(表现形式)
  • 可以根据程序需求覆盖该方法,如:展现对象各个属性值
public static void main(String[] args)
{
Student s1=new Student(23, "陈士虎");
Student s2=new Student(21,"陶雨");
Class c1=s1.getClass();
Class c2=s2.getClass();
System.out.println(s1.hashCode());
System.out.println(s2.hashCode());
Student s3=s2;
System.out.println(s3.hashCode());
System.out.println(s1.toString());
}
//打印结果
2143192188
204349222
204349222
陈士虎,23
equals()方法
  • public boolean equals(Object obj){}
  • 默认实现为(this==obj),比较两个对象地址是否相同
  • 可进行覆盖,比较两个对象的内容是否相同

equals()方法覆盖步骤

  • 比较两个引用是否指向同一个对象
  • 判断obj是否为null
  • 判断两个引用指向的实际对象类型是否一致
  • 强制类型转换
  • 依次比较各个属性值是否相同
public boolean equals(Object anObject)
{
//1判断两个对象是否是同一个引用
if(this==anObject)
{
return true;
}
//2判断anObject是否null
if(anObject==null)
return false;
//3判断是否是同一个类型
//	if(this.getClass()==anObject.getClass())
//	{
//
return true;
//	}
//instanceof可以判断对象是否是某类型的
if(this instanceof Student)
{//4强制类型转换
Student s=(Student)anObject;
}
if(this.name.equals(s.getName())&&this.age.equals(s.getAge()))
{
return true;
}
return false;
}
//打印结果为true

Student s4=new Student(20,"陶雨");
Student s5=new Student(20,"陶雨");
System.out.println(s4.equals(s5));//这是地址之间的比较flash
System.out.println(s4.toString().equals(s5.toString()));//这是字符串的比较true
finalize()方法
  • 当对象被判定为垃圾对象时。由JVM自动调用此方法,用以标记垃圾对象,进行回收队列

  • 垃圾对象:没有有效引用指向此对象时,为垃圾对象

  • 垃圾回收:由GC销毁垃圾对象,释放数据存储空间

  • 自动回收机制:JVM的内存耗尽,一次性回收所有垃圾对象

  • 手动回收机制:使用System.gc();通知JVM执行垃圾回收

public static void main(String[] args)
{
new Student(23, "陈士虎");
new Student(21,"陶雨");
//垃圾回收
System.gc();
}
//重写方法
protected void finalize() throws Throwable {
System.out.println(this.name+"被回收");
}

包装类

  • 基本数据类型所对应的引用数据类型
  • Object可统一所有数据类型,包装类的默认值是null
基本数据类型包装类
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
booleanBoolean
charCharacter
5.1 基本类型包装类概述
  • 将基本数据类型封装成对象好处在于可以在对象中定义更多的功能方法操作该数据
  • 常用的操作之一:用于基本数据类型雨字符串之间的转换
5.2 Integer类的概述和使用

Integer:包装一个对象中的原始类型int 的值

方法名说明
public static Integer valueOf(int i)返回表示指定的int值的Integer实例
public static Integer valueOf(String s)返回一个保存指定值的Integer对象String
public class day01
{
public static void main(String[] args) {
Integer i1=Integer.valueOf(100);
System.out.println(i1);
Integer i2=Integer.valueOf("100");
System.out.println(i2);
}
}
5.3 int和String的相互转换

基本类型包装类的最常见操作就是:用于基本类型和字符串的相互转换

  1. int 转换为String

​ public static String valueOf(int i):返回int参数的字符串表示形式,该方法时String类中的方法

  1. String转换为int

    public static int parslent(String s):将字符串解析为int类型该方法时Interger类中的方法

//int和String的相互转换
public class day01 {
public static void main(String[] args) {
//int----------String
int a = 100;
//方式一
String s1 = "" + a;
System.out.println(s1);
//方式二
String s2 = String.valueOf(a);
System.out.println(s2);
System.out.println("--------------------------------------");
//Stirng------int
String s="100";
//方式一
//String------Integer-----int
Integer i=Integer.valueOf(s);
int x=i.intValue();
System.out.println(x);
//方式二
int y=Integer.parseInt(s);
System.out.println(y);
}
}
案例:字符串数据排序
5.4自动装箱和拆箱
  • 装箱:把基本数据类型转换为对应的包装类类型
  • 拆箱:把包装类类型转换为对应的基本数据类型
public class test {
public static void main(String[] args) {
//1
装箱:把基本数据类型转换为对应的包装类类型
Integer i=Integer.valueOf(100);
Integer i1=100;//自动装箱,简化了这个操作隐藏了Integer.valueOf()
//2
拆箱:把包装类类型转换为对应的基本数据类型
//i1+=200;
//i1.intValue()就是拆箱的操作,把引用数据类型----基本数据类型
i1=i1.intValue()+200;
//自动拆箱,里面还包含了自动装箱
i1+=200;
}
}

注意:在使用包装类类型的时候,如果做操作,最好先判断是否为null

Date类

1 Date类概述和构造方法

Date代表了一个特定的时间,精确到毫秒

方法名说明
public Date()分配一个Date对象,并初始化,以便它代表它被分配的时间,精确到毫秒
public Date(long date)分配一个Date对象,并将其初始化为表示从标准基准时间起指定的毫秒数
import java.util.Date;
public class sa {
public static void main(String[] args) {
Date d1=new Date();
System.out.println(d1);
//打印结果:Tue Nov 23 15:21:04 CST 2021
long date =1000*60*60;
Date d2=new Date(date);
System.out.println(d2);
//打印结果:Thu Jan 01 09:00:00 CST 1970
}
}

2 Date类的常用方法

方法名说明
pubic long getTime()获取的是日期对象从1970年1月1日00:00:00到现在的毫秒值
public void setTime(long time)设置时间,给的是毫秒值

线程

线程介绍

程序

是为了完成特定人物、用某种语言编写的一组指令的集合。

​ 简单的来说,就是我们写的代码

进程

  1. 进程是指运行中的程序,比如我们使用的QQ,就启动了一个进程,操作系统就会为该进程分配内存空间,当我们使用迅雷,又启动了一个进程,操作系统将为迅雷分配新的内存空间
  2. 进程是程序的一次执行过程,或是正在运行的一个程序。是动态过程:有它自身的产生,存在和消亡那个的过程

线程

  1. 线程由进程创建的,是进程的一个实体
  2. 一个进程可以拥有多个线程,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nk8BpoZc-1640005796000)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211129081343741.png)])

单线程

同一个时刻,只允许执行一个进程

多线程

同一个时刻,可以执行多个线程,比如:一个qq进程,可以同时大开福哦个聊天窗口,一个迅雷进程,可以同时下载多个文件

并发

同一个时刻,多个任务交替执行,造成一种“貌似同时”的错觉,简单的来说,cpu实现的是多任务就是并发

并行

同一个时刻,多个任务同时执行。多核cpu可以实现并行

并发和并行也可能同时存在的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MPaYxYQb-1640005796001)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211129081320516.png)]

线程使用(重要)

1. 创建线程的两种方式

在java中线程来使用有两种方法

  1. 继承Thread类,重写run方法
  2. 实现Runnable接口,重写run方法

2. 线程应用案例1,继承Thread类

  1. 请编写程序,开启一个线程,该线程每隔一秒,在控制台输出“喵喵,我是小猫咪”
  2. 对上题改进:当输出80次 喵喵,我是小猫咪,结束该进程
  3. 使用JConsole监控线程 执行情况,并画出程序示意图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qMSwN52F-1640005796002)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211129152009229.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yva467b0-1640005796002)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211129164855274.png)]

public class Dou
{
/*
1.当一个类继承了Thread类,该类就是可以当作线程使用
2.我们会重写run方法,写上自己的业务代码
3.run Thread类,实现了Runnable接口的run方法
*/
public static void main(String[] args) throws InterruptedException {
Cat a=new Cat();
//这里不是run方法,而是start方法
a.start();//启动线程,这个最终会执行cat的run方法
//说明:当main线程启动一个子线程Thread-0,主线程不会阻塞,会继续执行
//这时,主线程和子线程是交替执行的
//这时,主线程和子线程是交替执行的
for (int i=0;i<5;i++)
{
System.out.println("主线程i="+i+"+"+Thread.currentThread().getName());
Thread.sleep(1000);
}
}
}
class Cat extends Thread
{
@Override
public void run() {
int w=0;
while (true) {
System.out.println("喵喵,我是一只小猫咪"+(++w)+"+"+"线程名称:"+Thread.currentThread().getName());
try {
//sleep是睡眠的方法
Thread.sleep(1000);//1000毫秒就是1秒
//快捷键ctrl+alt+t
} catch (InterruptedException e) {
e.printStackTrace();
}
if(w==80)
{
break;
}
}
}
}
/*打印结果
主线程i=0+main
喵喵,我是一只小猫咪1+线程名称:Thread-0
喵喵,我是一只小猫咪2+线程名称:Thread-0
主线程i=1+main
主线程i=2+main
喵喵,我是一只小猫咪3+线程名称:Thread-0
主线程i=3+main
喵喵,我是一只小猫咪4+线程名称:Thread-0
喵喵,我是一只小猫咪5+线程名称:Thread-0
主线程i=4+main
喵喵,我是一只小猫咪6+线程名称:Thread-0
喵喵,我是一只小猫咪7+线程名称:Thread-0
喵喵,我是一只小猫咪8+线程名称:Thread-0
喵喵,我是一只小猫咪9+线程名称:Thread-0
喵喵,我是一只小猫咪10+线程名称:Thread-0
*/

问题:为什么不用cat.run();而是cat.start();?

cat.run()就是一个普通的方法,如果这样的话,就先执行润run方法,之后在执行下面的方法,就不是多线程了;看下面代码


public class Dou
{
/*
1.当一个类继承了Thread类,该类就是可以当作线程使用
2.我们会重写run方法,写上自己的业务代码
3.run Thread类,实现了Runnable接口的run方法
*/
public static void main(String[] args) throws InterruptedException {
Cat a=new Cat();
//这里不是run方法,而是start方法
//a.start();//启动线程,这个最终会执行cat的run方法
a.run();
//说明:当main线程启动一个子线程Thread-0,主线程不会阻塞,会继续执行
//这时,主线程和子线程是交替执行的
//这时,主线程和子线程是交替执行的
for (int i=0;i<5;i++)
{
System.out.println("主线程i="+i+"+"+Thread.currentThread().getName());
Thread.sleep(1000);
}
}
}
class Cat extends Thread
{
@Override
public void run() {
int w=0;
while (true) {
System.out.println("喵喵,我是一只小猫咪"+(++w)+"+"+"线程名称:"+Thread.currentThread().getName());
try {
//sleep是睡眠的方法
Thread.sleep(1000);//1000毫秒就是1秒
//快捷键ctrl+alt+t
} catch (InterruptedException e) {
e.printStackTrace();
}
if(w==10)
{
break;
}
}
}
}
/*
喵喵,我是一只小猫咪1+线程名称:main
喵喵,我是一只小猫咪2+线程名称:main
喵喵,我是一只小猫咪3+线程名称:main
喵喵,我是一只小猫咪4+线程名称:main
喵喵,我是一只小猫咪5+线程名称:main
喵喵,我是一只小猫咪6+线程名称:main
喵喵,我是一只小猫咪7+线程名称:main
喵喵,我是一只小猫咪8+线程名称:main
喵喵,我是一只小猫咪9+线程名称:main
喵喵,我是一只小猫咪10+线程名称:main
主线程i=0+main
主线程i=1+main
主线程i=2+main
主线程i=3+main
主线程i=4+main
*/
/*start方法的分析
(一)
public synchronized void start()
{
start0();
}
(二)
//start0()是本地方法,是JVM调用,底层是C/c++实现
//真正实现多线程的效果,是start0();而不是run;
private native void start0();
*/

3. 线程应用案例2:-实现Runnable接口

说明

  1. java是单继承,在某些情况下一个类可能已经继承了某个父类,这时在用继承Thread类方法来创建线程显然不可能了
  2. java设计者们提供了另一个方式创建线程,就是通过实现Runnable接口来创建线程

应用案例

请编写程序,该程序可以每隔一秒,在控制台输出hi,当输出10次后,自动退出,

请使用实现Runnable接口的方式实现,Thread。。这里底层使用了设计模式【代理模式】

下面是静态代理的解析

public class Dou
{
public static void main(String[] args)
{
Tiger tiger = new Tiger();//实现了Runnable
ThreadProxy threadProxy = new ThreadProxy(tiger);
threadProxy.start();
}
}
class Animals{}
class Tiger extends Animals implements Runnable
{
@Override
public void run() {
System.out.println("老虎嗷嗷叫");
}
}
//线程代理类,模拟了一个极简的Thread类
class ThreadProxy implements Runnable
{
private Runnable target=null;//属性:类型是Runnable的
@Override
public void run()
{
if (target!=null)
{
target.run();//动态绑定(运行类型Tiger)
}
}
public ThreadProxy(Runnable target) {
this.target = target;
}
public void start()
{
start0();//这个方法是真正实现多线程的方法
}
public void start0()
{
run();
}
}
//老虎嗷嗷叫

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UcRzg1bR-1640005796003)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211129164923474.png)]

4. 线程使用应用案例-多线程执行

请编写一个程序,创建两个线程,一个线程每隔一秒输出“hello,world”,输出10次,退出。一个线程每隔一秒输出“hi”,输出5次退出,

public class Dou
{
public static void main(String[] args)
{
T1 t1=new T1();
T2 t2=new T2();
Thread thread1 = new Thread(t1);
Thread thread2 = new Thread(t2);
thread1.start();//启动第一个进程
thread2.start();//启动第二个进程
}
}
class T1 implements Runnable
{
@Override
public void run() {
int a=0;
while (true)
{
System.out.println("hello,world"+(++a)+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(a==10)
{
break;
}
}
}
}
class T2 implements Runnable
{
@Override
public void run() {
int a=0;
while (true)
{
System.out.println("hi"+(++a)+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(a==5)
{
break;
}
}
}
}
/*
hi1Thread-1
hello,world1Thread-0
hello,world2Thread-0
hi2Thread-1
hello,world3Thread-0
hi3Thread-1
hi4Thread-1
hello,world4Thread-0
hi5Thread-1
hello,world5Thread-0
hello,world6Thread-0
hello,world7Thread-0
hello,world8Thread-0
hello,world9Thread-0
hello,world10Thread-0
*/

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iHKBxClm-1640005796003)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211129165216082.png)]

5. 继承Tread vs 实现Runnable的区别

  1. 从java的设计来看,通过继承Thread或者实现Runnable接口来创建线程本质上没有区别,从JDK帮助文档我们可以看到Thread类本身就实现了Runn able接口

  2. 实现Runnable接口方式更加适合多个线程共享一个资源的情况,并且避免了单继承的限制**(建议使用这个)**[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r7RzUKFs-1640005796004)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211129165835309.png)]

  3. public class St
    {
    public static void main(String[] args) {
    Gu1 a=new Gu1();
    new Thread(a).start();
    new Thread(a).start();
    new Thread(a).start();
    }
    }
    class Gu1 implements Runnable
    {
    private int num=100;
    @Override
    public void run() {
    while (true)
    {
    System.out.println("窗口"+"+"+Thread.currentThread().getName()+"售出一张票"+"剩余票数"+(--num));
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    if(num<=0)
    {
    break;
    }
    }
    }
    }
    /*
    */
    public class Dou
    {
    public static void main(String[] args)
    {
    Gun a1 = new Gun();
    Gun a2 = new Gun();
    Gun a3 = new Gun();
    a1.start();
    a2.start();
    a3.start();
    }
    }
    class Gun extends Thread
    {
    private static int num=100;
    @Override
    public void run() {
    while (true)
    {
    System.out.println("售票结束。。。");
    try {
    Thread.sleep(50);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    if(num<=0)
    {
    break;
    }
    System.out.println("窗口"+"+"+Thread.currentThread().getName()+"售出一张票"+"剩余票数"+(--num));
    }
    }
    }
    

这两种都会出现超卖的情况

6. 线程终止

  • 基本说明
    1. 当线程完成任务后,会自动退出
    2. 还可以通过使用变量来控制run方法退出的方式停止线程,即通知方式

应用案例

需求:启动一个线程t,要求在main线程中去停止线程t,请编写实现

import java.util.TreeSet;
public class sa {
public static void main(String[] args) throws InterruptedException {
t1 e=new t1();
e.start();
//如果希望main线程去控制e线程的终止,必须可以修改loop
//让e退出run方法,从而终止e线程 ---通知方式
//让主线程休眠10秒
Thread.sleep(1000*10);
e.setI(false);
}
}
class t1 extends Thread
{
private int a=0;
//设置一个控制变量
private boolean i=true;
@Override
public void run() {
while (i) {
System.out.println("运行中" + "+" + (++a));
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void setI(boolean i) {
this.i = i;
}
}

线程10秒后就会退出线程

线程方法

常用方法(一)

方法名说明
setName设置线程名称,使之与参数name相同
getName返回该线程的名称
start使该线程开始执行;java虚拟机底层调用该线程的start0方法
run调用线程对象run方法
setPriority更改线程的优先级
getPriority获取线程的优先级
sleep在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
interrupt中断线程

注意细节

  1. start底层会创建新的线程,调用run,run就是一个简单的方法调用,不会启动新线程
  2. 线程优先级的范围
  3. interrupt,中断线程,但并没有真正的结束线程,所以一般用于中断正在休眠线程
  4. sleep:线程的静态方法,使当前线程休眠
import java.util.TreeSet;
public class sa {
public static void main(String[] args) throws InterruptedException {
t1 e=new t1();
e.setName("陶雨");
e.setPriority(Thread.MIN_PRIORITY);
e.start();
for(int i=0;i<5;i++)
{
Thread.sleep(1000);
System.out.println("hi"+i);
}
e.interrupt();//当执行到这里,就会中断t线程的休眠
}
}
class t1 extends Thread
{
private int a=0;
//设置一个控制变量
private boolean i=true;
@Override
public void run() {
while (i) {
for (int i = 0; i < 100; i++) {
System.out.println("吃包子" + "+" + (++a));
}
try {
System.out.println("休眠中");
Thread.sleep(10000);
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+"被interrupt了");
}
}
}
}

常用方法(二)

  1. yield:线程的礼让。让出cpu,让其它线程执行,但礼让的时间不确定,所以也并不一定礼让成功

  2. join:线程的插队,插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务

    案例:主线程创建一个子线程,每隔一秒输出hello,输出20次主线程每隔一秒,输出hi,输出20次,要求:两个线程同时执行,当主线程输出5次后,就让子线程运行完毕,主线程在继续。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JfRrc64x-1640005796004)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211129211613484.png)]

public class Dou
{
public static void main(String[] args) throws InterruptedException {
Gun e=new Gun();
e.start();
for (int i=0;i<=10;i++)
{
Thread.sleep(1000);
System.out.println("主线程(小弟)吃了"+i+"包子");
if(i==5)
{
System.out.println("主线程(小弟)让子线程(老大)先吃");
e.join();
System.out.println("子线程(老大)先吃完了");
}
}
}
}
class Gun extends Thread
{
@Override
public void run()
{
for (int i=0;i<=10;i++)
{
try
{
Thread.sleep(1000);
} catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println("子线程(老大)吃了" + i + "包子");
}
}
}
/*
子线程(老大)吃了0包子
主线程(小弟)吃了0包子
主线程(小弟)吃了1包子
子线程(老大)吃了1包子
子线程(老大)吃了2包子
主线程(小弟)吃了2包子
子线程(老大)吃了3包子
主线程(小弟)吃了3包子
子线程(老大)吃了4包子
主线程(小弟)吃了4包子
子线程(老大)吃了5包子
主线程(小弟)吃了5包子
主线程(小弟)让子线程(老大)先吃
子线程(老大)吃了6包子
子线程(老大)吃了7包子
子线程(老大)吃了8包子
子线程(老大)吃了9包子
子线程(老大)吃了10包子
子线程(老大)先吃完了
主线程(小弟)吃了6包子
主线程(小弟)吃了7包子
主线程(小弟)吃了8包子
主线程(小弟)吃了9包子
主线程(小弟)吃了10包子
*/
//如果换成礼让的话不一定成功

课堂练习

  1. 主线程每隔1秒输出hi,一共10次

  2. 当输出到hi5时,启动一个子线程(要求实现Runnable),每隔1秒输出hello,等该线程输出10次hello后,退出

  3. 主线程继承输出hi,直到主线程退出

    public class sa
    {
    public static void main(String[] args) throws InterruptedException {
    t1 a=new t1();
    Thread ar=new Thread(a);
    for(int i=1;i<=10;i++)
    {
    Thread.sleep(1000);
    System.out.println("hi"+i);
    if (i==5)
    {
    ar.start();
    ar.join();
    System.out.println("子线程结束。。。");
    }
    }
    System.out.println("主线程结束。。。");
    }
    }
    class t1 implements Runnable
    {
    @Override
    public void run()
    {
    for (int i=1;i<=10;i++)
    {
    System.out.println("hello"+i);
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    }
    }
    /*
    hi1
    hi2
    hi3
    hi4
    hi5
    hello1
    hello2
    hello3
    hello4
    hello5
    hello6
    hello7
    hello8
    hello9
    hello10
    子线程结束。。。
    hi6
    hi7
    hi8
    hi9
    hi10
    主线程结束。。。
    */
    

用户线程和守护线程

  1. 用户线程:也叫工作线程,当线程的任务执行完或通知方式结束

  2. 守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束

  3. 常见的守护线程:垃圾回收机制

  4. public class sa {
    public static void main(String[] args) throws InterruptedException {
    GU a=new GU();
    a.setDaemon(true);//这段代码要写在a.start();之前
    a.start();
    //如果我们希望当main线程结束后,子线程自动结束
    //只需把子线程设为守护线程即可
    //即 a.setDaemon(true);
    for (int i=0;i<=10;i++)
    {
    System.out.println("陈士虎在打游戏");
    Thread.sleep(1000);
    }
    }
    }
    class GU extends Thread
    {
    @Override
    public void run() {
    for(;;)
    {
    System.out.println("陶雨吃蛋糕");
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    }
    }
    

线程的声明周期[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c341F8AW-1640005796005)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211130150450135.png)]

  1. 刚创建,没启动
  2. 可运行状态(就绪状态,运行状态)
  3. 阻塞状态
  4. 等待状态
  5. 超时等待状态
  6. 终止状态

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-APeRhMab-1640005796006)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211130151445717.png)]

Synchronized

线程同步机制

  • 在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何同一时刻,最多有一个线程访问,以保证数据的完整性
  • 也可以这里理解,线程同步,即当有一个线程在对内存进进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作

同步具体方法

  • 同步代码块

  • Synchronized(对象)

    {

    得到对象的锁。才能操作同步代码

    需要被同步代码

    }

  • synchronized还可以放在方法声明中,表示整个方法–为同步方法

    public synchronized void m(String name)

    {

    需要被同步的代码

    }[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GEfWLPAf-1640005796006)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211130153920561.png)]

public class test
{
public static void main(String[] args) {
Gu1 a=new Gu1();
new Thread(a).start();
new Thread(a).start();
new Thread(a).start();
}
}
class Gu1 implements Runnable
{
private boolean loop =true;
private static int num=100;
public synchronized void sell()
{
if (num<=0)
{
System.out.println("售票结束。。。");
loop=false;
return;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("窗口"+"+"+Thread.currentThread().getName()+"售出一张票"+"剩余票数="+(--num));
}
@Override
public
void run()
{
while (loop) {
sell();//是一个同步方法
}
}
}

互斥锁

基本介绍

  1. java在java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性

  2. 每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象

  3. 关键字synchronized来与对象的互斥锁联系,当某个对象用synchronized修饰时,表明该对象在任已时刻只能由一个线程访问

  4. 同步的局限性:导致程序的执行效率要降低

  5. 同步方法(非静态)的锁可以是this,也可以是其他对象(要求是同一个对象)

  6. 同步方法(静态的)的锁为当前类本身

  7. public class test
    {
    public static void main(String[] args) {
    Gu1 a=new Gu1();
    new Thread(a).start();
    new Thread(a).start();
    new Thread(a).start();
    }
    }
    class Gu1 implements Runnable
    {
    //1. public sybcgronized void sell(){}
    //2. 这时锁在this对象
    //3. 也可以在代码块上写,synchronized,同步代码块
    private boolean loop =true;
    private static int num=100;
    public synchronized void sell()
    {
    synchronized(this) {
    if (num <= 0) {
    System.out.println("售票结束。。。");
    loop = false;
    return;
    }
    try {
    Thread.sleep(50);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println("窗口" + "+" + Thread.currentThread().getName() + "售出一张票" + "剩余票数=" + (--num));
    }
    }
    @Override
    public
    void run()
    {
    while (loop) {
    sell();//是一个同步方法
    }
    }
    }
    

这里的this也可以换成其他的对象,只要是同一个对象就可以。

public class test
{
public static void main(String[] args) {
Gu1 a=new Gu1();
new Thread(a).start();
new Thread(a).start();
new Thread(a).start();
}
//同步方法(静态的)的锁为当前类本身
//1. public synchronized static void m1(){}锁是加在test.Class
//2.如果在静态方法中,实现一个同步代码块 如m2()方法
public synchronized static void m1(){}
public static void m2()
{
synchronized (test.class){
System.out.println("陶雨的小皮皮");
}}
}
class Gu1 implements Runnable
{
//1. public sybcgronized void sell(){}
//2. 这时锁在this对象
//3. 也可以在代码块上写,synchronized,同步代码块
private boolean loop =true;
private static int num=100;
public synchronized void sell()
{
synchronized(this) {
if (num <= 0) {
System.out.println("售票结束。。。");
loop = false;
return;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("窗口" + "+" + Thread.currentThread().getName() + "售出一张票" + "剩余票数=" + (--num));
}
}
@Override
public
void run()
{
while (loop) {
sell();//是一个同步方法
}
}
}

注意事项

  1. 同步方法如果没有使用static修饰:默认锁对象为this
  2. 如果方法使用static修饰,默认锁对象:当前类.Class
  3. 实现的落地步骤
    • 需要先分析上锁的代码
    • 选择同步代码块(推荐)或同步方法
    • 要求多个线程的锁对象为同一个即可(用Runnable方式才可以把它锁住)

线程的死锁

基本介绍

多个线程都占用了对方的锁资源,但不肯相让,导致了死锁,在编程是一定要避免死锁的发生的

应用案例:

妈妈:你先完成作业,才让你玩手机

小明:你先让我玩手机,我才写作业

释放锁

下面操作会释放锁

  • 当前线程的同步方法、同步代码块执行结束

    案例:上厕所,完事出来

  • 当前线程在同步代码块、同步方法中遇到break、return

    案例:没有正常的完事,经理叫他修改bug。不得已出来

  • 当前线程在同步代码块、同步方法中出现了未处理的Error或Exception。导致异常结束

    案例:没有正常的完事,发现忘记带纸了,不得已出来

  • 当前线程在同步代码块、同步方法zh 执行了线程对象的wait()方法,当前线程暂停,并释放锁

    案例:没有正常完事,觉得需要酝酿一下,所以出去等会在进来

下面操作不会释放锁

  • 线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yielld()方法暂停当前线程的执行,不会释放锁

    案例:上厕所,太困了,下坑上眯了一会

  • 线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,不会释放锁

    提示:因尽量避免使用suspend()和resume()来控制线程,方法不在推荐使用

本章作业

1.编程题

  • 在main方法中启动两个线程

  • 第一个线程循环随机打印100以内的整数

  • 直到第二个线程从键盘随机读取了“Q”命令

  • package Day01;
    import java.util.Scanner;
    public class D02
    {
    public static void main(String[] args) {
    A a=new A();
    B b=new B(a);
    a.start();
    b.start();
    }
    }
    class A extends Thread
    {
    private boolean loop=true;
    @Override
    public void run() {
    while (loop) {
    System.out.println((int) (Math.random() * 100 + 1));
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    }
    public void setLoop(boolean loop) {
    this.loop = loop;
    }
    }
    class B extends Thread
    {
    private A a;
    private Scanner scanner=new Scanner(System.in);
    public B(A a) {
    this.a = a;
    }
    @Override
    public void run()
    {
    while (true) {
    System.out.println("请输出你指令(Q)表示退出");
    char key=scanner.next().toUpperCase().charAt(0);
    if(key=='Q')
    {
    a.setLoop(false);
    System.out.println("A线程退出");
    System.out.println("B线程退出");
    break;
    }
    }
    }
    }
    /*
    请输出你指令(Q)表示退出
    96
    94
    68
    42
    47
    20
    A82
    Q51
    41
    Q82
    63
    Q21
    9
    Q12
    90
    Q
    A线程退出
    B线程退出
    */
    

2.编程题

package Day01;
public class D03
{
public static void main(String[] args)
{
C a=new C();
new Thread(a).setName("A线程");
new Thread(a).setName("B线程");
new Thread(a).setName("C线程");
new Thread(a).start();
new Thread(a).start();
new Thread(a).start();
}
}
class C implements Runnable
{
private int mon=1000;
@Override
public void run() {
synchronized (this) {
while (mon>0)
{
System.out.println("取出了1000,还剩=" + (mon = mon - 100));
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(mon<=0)
{
System.out.println("余额不足");
}
}
}
}
}
/*
取出了1000,还剩=900
取出了1000,还剩=800
取出了1000,还剩=700
取出了1000,还剩=600
取出了1000,还剩=500
取出了1000,还剩=400
取出了1000,还剩=300
取出了1000,还剩=200
取出了1000,还剩=100
取出了1000,还剩=0
余额不足
*/

IO流

文件

什么是文件

文件,对我们并不陌生,文件是保存数据的地方,比如大家经常使用的word文档,。。。。它既可以保存一张图片,也可以保存视频

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iGzxAazJ-1640005796007)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211208174325904.png)]

流:数据在数据源(文件)和程序(内存)之间经历的路径

输入流:数据从数据源(文件)到程序(内存)的路径

输出流:数据从程序(内存)到数据源(文件)的路径

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZdFhMirf-1640005796007)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211208182817784.png)]

package D03;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.IOException;
public class D01
{
public static void main(String[] args) {
}
@Test
//方式一:new File(String pathnaem)
public void create()
{
String filepath ="e:\news1.txt";//盘符e要小写
File file = new File(filepath);
try {
file.createNewFile();
System.out.println("文件创建成功");
} catch (IOException e) {
e.printStackTrace();
}
}
//方式二:new File(File parent,String child);根据父目录文件+子路径构建
@Test
public void create01()
{
File parentFile=new File("e:\");
//这里的前面的那个是转义符,在程序中有的,或者是/的一个符号也可以
String fileName="news2.txt";
//这里的file对象,在java程序中,只是一个对象
//只有执行了createNewFile方法,才会真正的,在磁盘创建该文件
File file = new File(parentFile, fileName);
try {
file.createNewFile();
System.out.println("文件创建成功");
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
//方式三:new File(File parent,String child);根据父目录文件+子路径构建
public void create02()
{
String parentPath="e:\";
String fileName="news3.txt";
File file =new File(parentPath,fileName);
try {
file.createNewFile();
System.out.println("文件创建成功");
} catch (IOException e) {
e.printStackTrace();
}
}
}

获取文件的相关信息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KgcleuwP-1640005796009)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211208201536838.png)]

目录的操作和文件删除

mkdir创建一级目录,mkdirs创建多级目录,delete删除空目录或文件

package D03;
import org.junit.jupiter.api.Test;
import java.io.File;
public class D02 {
public static void main(String[] args) {
}
//判断e:\news1.txt 是否存在,如果存在就删除
@Test
public void m1() {
String filePath = "e:\news1.txt";
File file = new File(filePath);
if (file.exists()) {
if (file.delete()) {
System.out.println(filePath + "删除成功");
} else {
System.out.println(filePath + "删除失败");
}
} else {
System.out.println(filePath + "不存在");
}
}
//判断e:\news2是否存在,存在就删除,否则提示不存在
//这里我们需要体会到,在java编程中,目录也被当作文件
@Test
public void m2() {
String filePath = "e:\news1";
File file = new File(filePath);
if (file.exists()) {
if (file.delete()) {
System.out.println(filePath + "删除成功");
} else {
System.out.println(filePath + "删除失败");
}
} else {
System.out.println("该目录不存在");
}
}
//判断e:\demo\a\b\c目录是否存在,如果存在就提示已经存在,否则就创建
@Test
public void m3() {
String filePath = "e:\demo\a\b\c";
File file = new File(filePath);
if (file.exists())
//必须要用exists()方法,这个是多级目录的,exist是只有一级目录的创建
{
System.out.println(filePath + "存在");
} else {
if (file.mkdirs()) {
System.out.println(filePath + "创建成功");
}
else
{
System.out.println(filePath + "创建失败");
}
}
}
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h2ao9m2o-1640005796009)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211208204506218.png)]

Io六流原理及流的分类

  • 流的分类
    1. 按操作数据单位不同分为:字节流(8 bit)。字符流(按字符)

    2. 按数据流的流向不同分为:输入流,输出流

    3. 按流的角色的不同分为:节点流,处理流/包装流

抽象基类字节流字符流
输入流InputStreamReader
输出流OutputStreamWriter
  • java的io流共涉及40多个类,实际上非常规则,都是从如上4个抽象基类派生的
  • 由这四个类派生出来的子类名称都是以其父类名作为子后缀

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OAaEQf7p-1640005796010)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211209131328574.png)]

InputStream:字节输出流

InputStream抽象类是所有类字节输入流的超类

InputStream常用的子类

名字说明
FileputStream文件输入流
BufferedInputStream缓冲字节输入流
ObjectInputStream对象字节输入流

绘图技术

画圆

package Day01;
import javax.swing.*;
import java.awt.*;
//如何在面板上化圆
public class D04 extends JFrame//这个其实就是一个窗口
{
//定义一个面板
private MyPanel a=null;
public static void main(String[] args)
{
new D04();
}
public D04()
{
//初始化面板
MyPanel mp = new MyPanel();
this.add(mp);
this.setSize(400,300);
this.setVisible(true);
}
}
//1.定义一个面板MyPanel,要继承JPanel类,画图形,就在面板上画
class MyPanel extends JPanel
{
/*
1.MyPanel 对象就是一个画板
2,Graphics g把g理解成一支画笔
3.Graphics 提供了很多的绘图方法
*
* */
@Override
public void paint(Graphics g) {//绘图方法
super.paint(g);
g.drawOval(10,10,100,100);
}
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wR0ePdhS-1640005796010)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211201092150174.png)]

绘制图片的方法[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hsif68Jl-1640005796010)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211201134550972.png)]

package Day01;
import javax.swing.*;
import java.awt.*;
//如何在面板上化圆
public class D04 extends JFrame//这个其实就是一个窗口
{
//定义一个面板
private MyPanel a=null;
public static void main(String[] args)
{
new D04();
}
public D04()
{
//初始化面板
MyPanel mp = new MyPanel();
this.add(mp);
this.setSize(400,300);
this.setVisible(true);
}
}
//1.定义一个面板MyPanel,要继承JPanel类,画图形,就在面板上画
class MyPanel extends JPanel
{
/*
1.MyPanel 对象就是一个画板
2,Graphics g把g理解成一支画笔
3.Graphics 提供了很多的绘图方法
*
* */
@Override
public void paint(Graphics g) {//绘图方法
super.paint(g);
//圆
// g.drawOval(10,10,100,100);
//直线
g.drawLine(10,10,100,100);
//矩形边框
// g.drawRect(10,10,100,50);
//填充矩形
//设置画笔颜色
//
g.setColor(Color.cyan);
//
g.fillRect(10,10,100,100);
//填充椭圆
//
g.setColor(Color.cyan);
//
g.fillOval(10,10,20,50);
//画图片
//1.获取图片资源 ,/表示在该项目的根目录去获取zuo.jpg图片资源
//
Image a= Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/zuo.jpg"));
//
g.drawImage(a,10,10,600,1067,this);//this是当前的意思
//画字符串
//给画笔设置颜色和字体
//
g.setColor(Color.cyan);
//
g.setFont(new Font("隶书",Font.BOLD,50));
//
g.drawString("陈士虎",100,100);//这个坐标是字的左下角
}
}

java事件处理机制

怎么让小球受到键盘的控制,上下左右移动[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lSWi5i4l-1640005796010)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211201192140607.png)]

事件处理机制深入理解

  1. 前面我们提到几个重要的概念,事件源、事件、事件监听器,我们下面来全面的介绍他们
  2. 事件源:事件源是一个产生事件的对象,比如按钮,窗口等
  3. 事件:事件就是承载事件源状态改变时的对象,比如当键盘事件、鼠标事件、窗口事件等等,会产生一个事件对象,该对象保存着当前事件很多信息,比如KeyEvent对象有含义被按下的Code值,java.awt.event包和javax.swing.enent包中定义了各种事件类型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IHwKibzN-1640005796011)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211201193213907.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bDn7YZna-1640005796012)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211201193426796.png)]

本章作业[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PwRrbfT0-1640005796012)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211201203001788.png)]

(推荐)或同步方法

  • 要求多个线程的锁对象为同一个即可(用Runnable方式才可以把它锁住)

线程的死锁

基本介绍

多个线程都占用了对方的锁资源,但不肯相让,导致了死锁,在编程是一定要避免死锁的发生的

应用案例:

妈妈:你先完成作业,才让你玩手机

小明:你先让我玩手机,我才写作业

释放锁

下面操作会释放锁

  • 当前线程的同步方法、同步代码块执行结束

    案例:上厕所,完事出来

  • 当前线程在同步代码块、同步方法中遇到break、return

    案例:没有正常的完事,经理叫他修改bug。不得已出来

  • 当前线程在同步代码块、同步方法中出现了未处理的Error或Exception。导致异常结束

    案例:没有正常的完事,发现忘记带纸了,不得已出来

  • 当前线程在同步代码块、同步方法zh 执行了线程对象的wait()方法,当前线程暂停,并释放锁

    案例:没有正常完事,觉得需要酝酿一下,所以出去等会在进来

下面操作不会释放锁

  • 线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yielld()方法暂停当前线程的执行,不会释放锁

    案例:上厕所,太困了,下坑上眯了一会

  • 线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,不会释放锁

    提示:因尽量避免使用suspend()和resume()来控制线程,方法不在推荐使用

本章作业

1.编程题

  • 在main方法中启动两个线程

  • 第一个线程循环随机打印100以内的整数

  • 直到第二个线程从键盘随机读取了“Q”命令

  • package Day01;
    import java.util.Scanner;
    public class D02
    {
    public static void main(String[] args) {
    A a=new A();
    B b=new B(a);
    a.start();
    b.start();
    }
    }
    class A extends Thread
    {
    private boolean loop=true;
    @Override
    public void run() {
    while (loop) {
    System.out.println((int) (Math.random() * 100 + 1));
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    }
    public void setLoop(boolean loop) {
    this.loop = loop;
    }
    }
    class B extends Thread
    {
    private A a;
    private Scanner scanner=new Scanner(System.in);
    public B(A a) {
    this.a = a;
    }
    @Override
    public void run()
    {
    while (true) {
    System.out.println("请输出你指令(Q)表示退出");
    char key=scanner.next().toUpperCase().charAt(0);
    if(key=='Q')
    {
    a.setLoop(false);
    System.out.println("A线程退出");
    System.out.println("B线程退出");
    break;
    }
    }
    }
    }
    /*
    请输出你指令(Q)表示退出
    96
    94
    68
    42
    47
    20
    A82
    Q51
    41
    Q82
    63
    Q21
    9
    Q12
    90
    Q
    A线程退出
    B线程退出
    */
    

2.编程题

package Day01;
public class D03
{
public static void main(String[] args)
{
C a=new C();
new Thread(a).setName("A线程");
new Thread(a).setName("B线程");
new Thread(a).setName("C线程");
new Thread(a).start();
new Thread(a).start();
new Thread(a).start();
}
}
class C implements Runnable
{
private int mon=1000;
@Override
public void run() {
synchronized (this) {
while (mon>0)
{
System.out.println("取出了1000,还剩=" + (mon = mon - 100));
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(mon<=0)
{
System.out.println("余额不足");
}
}
}
}
}
/*
取出了1000,还剩=900
取出了1000,还剩=800
取出了1000,还剩=700
取出了1000,还剩=600
取出了1000,还剩=500
取出了1000,还剩=400
取出了1000,还剩=300
取出了1000,还剩=200
取出了1000,还剩=100
取出了1000,还剩=0
余额不足
*/

IO流

文件

什么是文件

文件,对我们并不陌生,文件是保存数据的地方,比如大家经常使用的word文档,。。。。它既可以保存一张图片,也可以保存视频

[外链图片转存中…(img-iGzxAazJ-1640005796007)]

流:数据在数据源(文件)和程序(内存)之间经历的路径

输入流:数据从数据源(文件)到程序(内存)的路径

输出流:数据从程序(内存)到数据源(文件)的路径

[外链图片转存中…(img-ZdFhMirf-1640005796007)]

package D03;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.IOException;
public class D01
{
public static void main(String[] args) {
}
@Test
//方式一:new File(String pathnaem)
public void create()
{
String filepath ="e:\news1.txt";//盘符e要小写
File file = new File(filepath);
try {
file.createNewFile();
System.out.println("文件创建成功");
} catch (IOException e) {
e.printStackTrace();
}
}
//方式二:new File(File parent,String child);根据父目录文件+子路径构建
@Test
public void create01()
{
File parentFile=new File("e:\");
//这里的前面的那个是转义符,在程序中有的,或者是/的一个符号也可以
String fileName="news2.txt";
//这里的file对象,在java程序中,只是一个对象
//只有执行了createNewFile方法,才会真正的,在磁盘创建该文件
File file = new File(parentFile, fileName);
try {
file.createNewFile();
System.out.println("文件创建成功");
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
//方式三:new File(File parent,String child);根据父目录文件+子路径构建
public void create02()
{
String parentPath="e:\";
String fileName="news3.txt";
File file =new File(parentPath,fileName);
try {
file.createNewFile();
System.out.println("文件创建成功");
} catch (IOException e) {
e.printStackTrace();
}
}
}

获取文件的相关信息

[外链图片转存中…(img-KgcleuwP-1640005796009)]

目录的操作和文件删除

mkdir创建一级目录,mkdirs创建多级目录,delete删除空目录或文件

package D03;
import org.junit.jupiter.api.Test;
import java.io.File;
public class D02 {
public static void main(String[] args) {
}
//判断e:\news1.txt 是否存在,如果存在就删除
@Test
public void m1() {
String filePath = "e:\news1.txt";
File file = new File(filePath);
if (file.exists()) {
if (file.delete()) {
System.out.println(filePath + "删除成功");
} else {
System.out.println(filePath + "删除失败");
}
} else {
System.out.println(filePath + "不存在");
}
}
//判断e:\news2是否存在,存在就删除,否则提示不存在
//这里我们需要体会到,在java编程中,目录也被当作文件
@Test
public void m2() {
String filePath = "e:\news1";
File file = new File(filePath);
if (file.exists()) {
if (file.delete()) {
System.out.println(filePath + "删除成功");
} else {
System.out.println(filePath + "删除失败");
}
} else {
System.out.println("该目录不存在");
}
}
//判断e:\demo\a\b\c目录是否存在,如果存在就提示已经存在,否则就创建
@Test
public void m3() {
String filePath = "e:\demo\a\b\c";
File file = new File(filePath);
if (file.exists())
//必须要用exists()方法,这个是多级目录的,exist是只有一级目录的创建
{
System.out.println(filePath + "存在");
} else {
if (file.mkdirs()) {
System.out.println(filePath + "创建成功");
}
else
{
System.out.println(filePath + "创建失败");
}
}
}
}

[外链图片转存中…(img-h2ao9m2o-1640005796009)]

Io六流原理及流的分类

  • 流的分类
    1. 按操作数据单位不同分为:字节流(8 bit)。字符流(按字符)

    2. 按数据流的流向不同分为:输入流,输出流

    3. 按流的角色的不同分为:节点流,处理流/包装流

抽象基类字节流字符流
输入流InputStreamReader
输出流OutputStreamWriter
  • java的io流共涉及40多个类,实际上非常规则,都是从如上4个抽象基类派生的
  • 由这四个类派生出来的子类名称都是以其父类名作为子后缀

[外链图片转存中…(img-OAaEQf7p-1640005796010)]

InputStream:字节输出流

InputStream抽象类是所有类字节输入流的超类

InputStream常用的子类

名字说明
FileputStream文件输入流
BufferedInputStream缓冲字节输入流
ObjectInputStream对象字节输入流

绘图技术

画圆

package Day01;
import javax.swing.*;
import java.awt.*;
//如何在面板上化圆
public class D04 extends JFrame//这个其实就是一个窗口
{
//定义一个面板
private MyPanel a=null;
public static void main(String[] args)
{
new D04();
}
public D04()
{
//初始化面板
MyPanel mp = new MyPanel();
this.add(mp);
this.setSize(400,300);
this.setVisible(true);
}
}
//1.定义一个面板MyPanel,要继承JPanel类,画图形,就在面板上画
class MyPanel extends JPanel
{
/*
1.MyPanel 对象就是一个画板
2,Graphics g把g理解成一支画笔
3.Graphics 提供了很多的绘图方法
*
* */
@Override
public void paint(Graphics g) {//绘图方法
super.paint(g);
g.drawOval(10,10,100,100);
}
}

[外链图片转存中…(img-wR0ePdhS-1640005796010)]

绘制图片的方法[外链图片转存中…(img-Hsif68Jl-1640005796010)]

package Day01;
import javax.swing.*;
import java.awt.*;
//如何在面板上化圆
public class D04 extends JFrame//这个其实就是一个窗口
{
//定义一个面板
private MyPanel a=null;
public static void main(String[] args)
{
new D04();
}
public D04()
{
//初始化面板
MyPanel mp = new MyPanel();
this.add(mp);
this.setSize(400,300);
this.setVisible(true);
}
}
//1.定义一个面板MyPanel,要继承JPanel类,画图形,就在面板上画
class MyPanel extends JPanel
{
/*
1.MyPanel 对象就是一个画板
2,Graphics g把g理解成一支画笔
3.Graphics 提供了很多的绘图方法
*
* */
@Override
public void paint(Graphics g) {//绘图方法
super.paint(g);
//圆
// g.drawOval(10,10,100,100);
//直线
g.drawLine(10,10,100,100);
//矩形边框
// g.drawRect(10,10,100,50);
//填充矩形
//设置画笔颜色
//
g.setColor(Color.cyan);
//
g.fillRect(10,10,100,100);
//填充椭圆
//
g.setColor(Color.cyan);
//
g.fillOval(10,10,20,50);
//画图片
//1.获取图片资源 ,/表示在该项目的根目录去获取zuo.jpg图片资源
//
Image a= Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/zuo.jpg"));
//
g.drawImage(a,10,10,600,1067,this);//this是当前的意思
//画字符串
//给画笔设置颜色和字体
//
g.setColor(Color.cyan);
//
g.setFont(new Font("隶书",Font.BOLD,50));
//
g.drawString("陈士虎",100,100);//这个坐标是字的左下角
}
}

java事件处理机制

怎么让小球受到键盘的控制,上下左右移动[外链图片转存中…(img-lSWi5i4l-1640005796010)]

事件处理机制深入理解

  1. 前面我们提到几个重要的概念,事件源、事件、事件监听器,我们下面来全面的介绍他们
  2. 事件源:事件源是一个产生事件的对象,比如按钮,窗口等
  3. 事件:事件就是承载事件源状态改变时的对象,比如当键盘事件、鼠标事件、窗口事件等等,会产生一个事件对象,该对象保存着当前事件很多信息,比如KeyEvent对象有含义被按下的Code值,java.awt.event包和javax.swing.enent包中定义了各种事件类型

[外链图片转存中…(img-IHwKibzN-1640005796011)]

[外链图片转存中…(img-bDn7YZna-1640005796012)]

本章作业[外链图片转存中…(img-PwRrbfT0-1640005796012)]

最后

以上就是可耐耳机为你收集整理的javaseJava基础总结一、程序设计基础二、数组冒泡排序三、方法四、面向对象(基础)封装继承多态static关键字final关键字项目–零钱通本章作业项目–房屋出租系统面向对象(高级)抽象类接口枚举和注解异常机制集合基础集合进阶常用类线程IO流绘图技术IO流绘图技术的全部内容,希望文章能够帮你解决javaseJava基础总结一、程序设计基础二、数组冒泡排序三、方法四、面向对象(基础)封装继承多态static关键字final关键字项目–零钱通本章作业项目–房屋出租系统面向对象(高级)抽象类接口枚举和注解异常机制集合基础集合进阶常用类线程IO流绘图技术IO流绘图技术所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(41)

评论列表共有 0 条评论

立即
投稿
返回
顶部