javaSE全阶段

javaSE阶段

    • 第一天
        • 1、进制转换
        • 2、字节和位
        • 3、数据类型分类
        • 4、标识符
        • 5、整数型
        • 6、 字符类型和字符集
        • 7、基础数据类型变量定义的注意事项
    • 第二天
        • 1、类型转换
        • 2、常量
        • 3、两种常见的输出语句
        • 4、运算符
          • 1.算术运算符
          • 2.+号的使用
          • 3.自增自减运算符
          • 4.赋值运算符
          • 5.关系运算符
          • 6.逻辑运算符
          • 7.短路逻辑运算符
          • 8.三元运算符
          • 9.位运算符
          • 10.移位运算符<<
        • 5、原码反码补码
    • 第三天
        • 1、流程语句与控制语句
          • 1.流程语句
          • 2.控制语句
            • 1、dowhile语句的格式:
            • 2、for语句的基本格式:
            • 3、if语句格式
            • 4、switch语句的基本格式:
            • 5、while循环的格式:
        • 2、运算符优先级的注意事项
  • 第一天(day04--5.31)
        • 1、方法(不调用,不执行)
          • 1.方法的定义
          • 2.方法的调用
          • 3.重载
        • 2、递归
        • 3、for循环嵌套
        • 4、死循环
  • 第二天(day05--6.1)
          • 1、数组
          • 2、java的内存划分
  • 第三天(day06--6.2)【6.10】
          • 1、冒泡排序
          • 2、数组反转
          • 3、方法进行传参的特点
          • 4、可变参数(jdk5.0)
          • 5、数组的折半查找(二分查找)
          • 6、数组的动态扩容、插入、删除
          • 7、二维数组
      • *思考:
    • 第四天(day07--6.6)
        • 1、什么是面向对象
        • 2、类和对象
          • 1.类的定义
            • 1.类的成员和内容
          • 2.对象的定义
        • 3、实例(成员)变量和局部变量区别
        • 4、this关键字的用法
          • 1.应用场景
          • 2.应用场景
        • 5、封装
          • 1.封装性的体现:
          • 2.修饰符
          • 3.封装的步骤
        • 6、构造方法(构造器)
        • 7、JavaBean标准类
        • 8、补充
          • 1.匿名对象
          • 2.私有方法
          • 3.构造代码块
  • (day08)(6.7)
        • 1、static关键字
          • 1.类变量(静态变量)
            • 1)三种变量的区别(静态变量,实例变量,局部变量)
          • 2.静态方法
          • 3.静态方法调用的注意事项:
          • 5.静态原理图解
          • 6.静态代码块
        • 2、单例设计模式
          • 1.立即加载(饿汉式)
          • 2.延迟加载(懒汉式)
        • 3、跨包使用类(import关键字)和API文档
          • 1.import关键字
          • 2.API
        • 4、API中的类
          • 1、Scanner类
          • 2、Math类
          • 3、Arrays类
          • 4、System类
          • 5、BigInteger类
          • 6、BigDecimal类
  • day09(6.8)
        • 1、继承
          • 1.继承的含义
          • 2.继承的格式
          • 3.继承的特点
  • **继承图解:walking:(进度)**
        • 2、super关键字和this的几种用法
        • 3、覆盖重写
          • 1.注意事项
        • 4、object类
          • 1.toString()方法
          • 2.equals()方法
  • day10(6.10)
        • 1、权限访问级别
          • 权限访问级别的补充:
        • 2、抽象
          • 1.抽象方法
          • 2.抽象类
          • 3.抽象方法和抽象类的使用:
          • 4、抽象方法和抽象类的用处
          • 5、注意事项
        • 3、final关键字
          • 1.修饰一个类
          • 2.修饰一个方法
          • 3.修饰一个局部变量
          • 4.修饰一个成员变量(实例变量和静态变量)
        • 4、接口
          • 1.概念
          • 2.定义格式
          • 3、接口成员说明
            • 1.公共的静态常量
            • 2.公共的抽象方法
            • 3.默认方法((JDK1.8以上)
            • 4.公共的静态方法(JDK1.8以上)
            • 5.私有方法(JDK1.9以上)
          • 4、接口的实现
            • 使用接口注意事项:
  • 总结
        • 1、在类中有的东西
          • 1.成员量(成员变量、成员常量)
          • 2.成员方法
          • 3.成员内部类
        • 2、在方法中的东西
        • 方法的概念(不调用,不执行)
        • 重载
          • 1.局部变量(方法中或方法声明上)【没有默认值,在栈内存,属于方法,只能被final关键字修饰】
          • 2.局部常量:被final修饰,这个变量就不可改变
          • 3.局部内部类
        • 3、接口中有的东西
          • 1.静态常量
          • 2.抽象方法
          • 3.默认方法(JDK1.8)
          • 4.静态方法(JDK1.8)
          • 5.私有方法(JDK1.9)
          • 6.内部接口
        • 4、javaBean标准类中有的
          • 构造器
        • 5、关系
        • 修饰符
      • **面向对象三大特征:封装、继承、多态**
        • 1、封装
        • 2、继承(extends)
        • 3、多态
        • 4、 几个关键字
          • static
          • final
          • this
          • super
          • abstract
        • 5、 引用数据类型:
        • 6、枚举类:
  • day11(6.11)
        • 1、多态
          • 1.多态的概述
          • 2.程序中的多态:
          • 3.多态的前提条件
          • 4.多态关系下实例方法的特点:
          • 5.多态的好处和弊端
          • 6.引用类型转型
          • 7.instanceof关键字:??
        • 2、内部类(重点:匿名内部类)
          • 1.内部类的形式
            • 1)成员内部类
            • 2)局部内部类
        • (2)匿名的内部类[重点 !*]
        • 3、枚举类
  • day12(6.13)
        • 1、类的初始化
        • 2、包装类
          • 1.拆箱和装箱:
            • 1.装箱的方式:
            • 2.拆箱的方式:
          • 2.基本类型数据和字符串类型数据的转换
            • 1.基本类型数据转换成字符串类型数据的方式:
            • 2.字符串类型数据转换成基本类型数据的方式:
            • 3.注意事项:
        • 3、包装类的面试题
  • day13(6.14)
        • 1、String类(java.lang)
          • String对象的创建方式:
          • 类的方法
          • StringBuffer类和StringBuilder类的相同点和不同点:
          • StringBuilder类
          • StringBuilder类的源码分析:
  • day14(6.15)
        • 1、String的面试题
        • 32、异常
          • 1.Throwable类(java.lang)
          • 2.异常的分类:
            • 1.运行时异常:
            • 2.编译时异常
          • 3.异常处理:
            • 1.运行时异常的处理方案:
            • 2.编译时异常的处理方案:
          • 4.关于异常的关键字
            • 1.throw关键字:
            • 2.finally关键字
          • 5、自定义异常:
            • 1.自定义运行时异常:
            • 2.自定义编译时异常:
  • day15(6.17)---day16(6.18)
        • 1、日期时间类
          • 1.Date类 (java.util)
            • 1.格式化:Date对象==>文本(String)
            • 2.解析:文本(String)==>Date对象
          • 2.DateFormat类 (java.text)
          • 3.Calendar类(java.util)
          • 4.LocalDateTime类(java.time)
          • 5.DateTimeFormatter类(java.time.format)【底层final修饰,不能创建对象以及不能被继承】
            • 1.LocalDateTime对象的格式化
            • 2.LocalDateTime对象的解析
        • 2、File (java.io)
        • 3、IO流
          • 1、字节流
            • 1.FileInputStream类(文件字节输入流)(java.io)
            • 2.FileOutputStream类(文件字节输出流)(java.io)
          • 2、字符流
            • 1.FileReader类(文件字符输入流)
            • 2.FileWriter类(文件字符输出流)
          • 3、缓冲流 (java.io)
            • 1.BufferedInputStream类(缓冲字节输入流)
            • 2.BufferedOutputStream类(缓冲字节输入流)
            • 3.BufferedReader类(缓冲字符输入流)
            • 4.BufferedWriter类(缓冲字符输出流)
          • 4、转换流
            • 1.InputStreamReader类(转换输入流)
            • 2.OutputStreamWriter类(转换输出流)
          • 5、对象流(序列化和反序列化)
            • 1.ObjectOutputStream类(对象输出流,序列化过程)
            • 2.Serializable接口(java.io)
            • 3.ObjectInputStream类(对象输入流,反序列化)
            • 4.对象流的注意事项:
            • 5.transient关键字:
          • 6、标准流
          • 7、IO流的关闭规则:
  • day17(6.20)
        • 1、集合(集合都是现在util包下)
        • 2、collection接口
          • 1.collection接口(java.util)
          • 2.collection集合的面试题
        • 3、迭代器(Iterator)
        • 4、泛型(jdk5.0)
          • 1.泛型的基础应用
            • 1.含有泛型的接口
            • 2、含有泛型的类
            • 3、含有泛型的方法
          • 2.泛型的高级应用:通配符
        • 5、List接口
          • 1.接口的特点
          • 2.接口的方法
          • 3.List集合的遍历方式:6种
        • 6、ArrayList类
          • 1.类的特点
          • 2.ArrayList集合的源码分析(基于JDK8.0):
            • 1.ArrayList集合底层数组的初始容量
            • 2.无参构造器的扩容原理:
            • 3.JDK6.0(包含)以前和JDK7.0(包含)以后的区别:
  • day18(6.21)
        • 1、Vector类
        • 2.LinkedList类
      • 3、set接口
        • 1、HashSet类
          • HashSet相关面试题
            • 1、HashSet集合如何过滤重复元素
            • 2、重写hashCode()时重要参数为什么是31?
        • 2、LinkedHashSet类
        • 3、红黑树数据结构:
        • 4、TreeSet类
      • Collectons类的方法
  • day19(6.22)-day20(6.23)
      • 1、Map(接口)集合
        • 1.HashMap类
        • 2、HashMap集合元素存储过程的源码分析(基于JDK8.0):
          • 1.相关名词解释
          • 2.涉及成员变量,成员常量和方法的局部变量的备忘录
          • 3.底层数组的初始容量和初始加载因子
          • 5.如何确认元素在底层数组中的索引位置
          • 4.默认的加载因子为什么是0.75?
        • 2.LinkedHashMap类
        • 3.TreeMap类
        • 4.Hashtable类
        • 5.HashMap和Hashtable之间的区别:
        • 6.Properties类
      • 2、多线程
        • 1、Thread类(java.lang)
          • 1.线程开启的方式:(4种)
            • 1.继承Thread类
            • 2.实现Runnable接口
        • 2.线程安全问题(线程同步问题)
      • 3.同步代码块和同步方法
        • 1.同步代码块
        • 2.同步方法
      • 4.线程间通信(等待唤醒机制)
      • 单例设计模式线程问题:
      • 4、多线程面试题
        • 1.Java中线程的状态分为几种?分别是?
        • 2.Java中线程如果进行状态的转化?
  • day21.网络编程(6.25)
      • 1、Commons-io工具包(IO流扩展)
      • 2、Lombok的使用
        • 1.@Getter和@Setter
        • 2.@ToString
        • 3.@NoArgsConstructor和@AllArgsConstructor
        • 4.@EqualsAndHashCode
        • 5.@Data
      • 3.网络编程
        • 1.通信三要素
          • 1.三次握手:
          • 2.四次挥手:
        • 2.实现简单客户端和服务端的交互
        • 1.编写客户端
        • 2.编写服务端
  • day22(6.26)
      • 1、函数式编程思想和Lambda表达式定义格式
        • 1、函数式思想
        • 2、Lamdba表达式
          • 1.适用前提:
          • 2.lambda表达式的格式:
          • 3.Lambda表达式省略规则
      • 3.函数式接口
        • 1.定义格式:
        • 2.java提供的几个常用接口
          • 1.Supplier
          • 2.Consumer
          • 3.Function
          • 4.Predicate
      • 4.Stream流
        • 1.数组和集合的获取:
        • 2.方法:
          • 1.Stream中的forEach方法:void forEach(Consumer action);
          • 2.Stream中的long count()方法
          • 3.Stream中的Stream filter(Predicate predicate)方法
          • 4.Stream limit(long maxSize):获取Stream流对象中的前n个元素,返回一个新的Stream流对象
          • 5.Stream skip(long n): 跳过Stream流对象中的前n个元素,返回一个新的Stream流对象
          • 6.static Stream concat(Stream a, Stream b):两个流合成一个流
          • 7.将Stream流变成集合
      • 5.方法的引用
      • 方法引用的介绍
        • 1.对象名--引用成员方法
        • 2.类名--引用静态方法
        • 3.类--构造引用
        • 4.数组--数组引用
  • day23(6.28)反射
      • 1、Junit单元测试
        • 1.介绍
        • 2.基本使用
        • 3.注意事项
        • 4.相关注解
        • 5.使用方法
      • 2、类的加载时机
      • 3、反射
        • 1.获取class对象的几种方式:
          • 1)案例《通过反射获取properties文件》
        • 2、获取Class对象中的构造方法
          • 1)获取所有的public构造方法
          • 2)获取空参的构造方法
          • 3)通过无参快速创建对象的快捷方式
          • 4)用反射获取有参构造方法并创建对象(public)
        • 3、反射方法
          • 1)获取所有的成员方法
          • 2)获取方法(有参、无参)
          • 3)小练习
      • 4、注解
        • 1.介绍
        • 2.注解的定义以及属性的定义格式
        • 3.注解的使用
        • 4.注解注意事项:
        • 5、注解解析的方法->AnnotatedElement接口
      • 5、元注解
        • 解析代码(两次)

第一天

1、进制转换

**十进制数据转成二进制数据:**使用除以2获取余数的方式

**二进制数据转成十进制数据:**使用8421编码的方式

2、字节和位

**字节(Byte):**是计算机信息技术用于计量存储容量的一种计量单位,通常情况下一字节等于有八

位, 也表示一些计算机编程语言中的数据类型和语言字符。

**位(bit):**是数据存储的最小单位。也就是二进制。二进制数系统中,每个0或1就是一个位,叫

做bit(比特),其中8 bit 就称为一个字节(Byte)。

转换关系:

​ 8 bit = 1 Byte

​ 1024 Byte = 1 KB

​ 1024 KB = 1 MB

​ 1024 MB = 1 GB

​ 1024 GB = 1 TB

常用cmd命令

命令 操作符号
盘符切换命令 盘符名:
查看当前文件夹内容 dir
进入文件夹命令 cd 文件夹名
退出文件夹命令 cd …
退出到磁盘根目录 cd \
清屏 cls
退出 exit

3、数据类型分类

基本数据类型:包括 整数 、 浮点 、 字符 、 布尔 。

引用数据类型:包括 类 、 数组 、 接口 。

4、标识符

标识符:是指在程序中,我们自己定义内容。比如类的名字、方法的名字和变量的名字等等,都是 标识符。

命名规则: 硬性规定

​ 标识符可以包含 英文字母26个(区分大小写) 、 0-9数字 、 $(美元符号) 和 _(下划线) 等。

​ 标识符不能以数字开头。

​ 标识符不能是关键字。

命名规范: 软性要求

​ 包名规范:

​ 多个英文单词:开头为公司域名的倒序,每个单词之间用 . 进行连接,例如:

​ com.atguigu.demo01

​ 类名规范:

​ 一个英文单词:单词首字母大写,例如:Dog

​ 多个英文单词:每个单词首字母大写(大驼峰式),例如:HelloWorld

​ 方法名规范:

​ 一个英文单词:单词字母全部小写,例如:main

​ 多个英文单词:第一个单词首字母小写,第二个单词以后(包含)每个单词首字母大写

​ (小驼峰式);例如:getMax,getSum,getName

​ 变量名规范:

​ 一个英文单词:单词字母全部小写,例如:result

​ 多个英文单词:第一个单词首字母小写,第二个单词以后(包含)每个单词首字母大写

​ (小驼峰式),例如:maxNum

​ 自定义常量名:

​ 一个英文单词:单词字母全部大写,例如:PI

​ 多个英文单词:单词字母全部大写,每个单词之间用下划线 _ 进行连接,例如:

​ DEFAULT_CAPACITY

5、整数型

int是最常用的整数类型,因此在通常请下,直接给出一个整数值默认就是int类型。除此之外,有

如下三种情形必须指出。

​ 1)如果直接将一个较小的整数值(在byte或short类型的取值范围内)赋给一个byte或者short类 型的变量,JVM会自动把这个整数值转换成byte或者short类型来处理。

​ 2)如果使用一个巨大的整数值(超出了int类型的取值范围,且在long的取值范围内)时,JVM不会自动把这个整数值当成long类型处理。如果希望JVM把一个整数值当成long类型来处理,应

在这个整数值后增加1或者L作为后缀。通常推荐使用L,因为英文字母l很容易跟数字1搞混。

​ 3)如果把一个较小的整数值(在int类型的取值范围以内)直接赋给一个long类型的变量,这并不是因为JVM会把这个较小的整数值当成long类型来处理,JVM依然把这个整数值当成int类型来处理,只是因为int类型的值会自动类型转换到long类型。(类型转换后面讲解)

Java中整数值有4种表示方式:

​ 十进制

​ 二进制,Java7.0种新增的特性,以0b或0B开头。

​ 八进制,以0开头

​ 十六进制,以0x或者0X开头,其中1015分别以af(此处的a~f不区分大小写)来表示。

6、 字符类型和字符集

字符集:在计算机的内部都是二进制的0、1数据,为了让计算机可以直接识别人类文字,将人类的

文字和一个十进制数进行对应起来组成一张表格。

字符集是各种文字和符号的总称,包括各国家文字、标点符号、图形符号、数字等。字符集

(Character set)是多个字符的集合,常见字符 集名称:ASCII字符集、GB2312字符集、GBK字符集、 GB18030字符集、Unicode字符集等。计算 机要准确的处理各种字符集文字,需要进行字符编码,以便计算机能够识别和存储各种文字。

将所有的英文字母,数字,符号都和十进制进行了对应,因此产生了世界上第一张编码表ASCII

(American Standard Code for Information Interchange 美国标准信息交换码)。

Unicode(又称统一码、万国码、单一码)是计算机科学领域里的一项业界标准,包括字符集、编

码方案等。Java语言 内部使用16位的Unicode字符集作为编码方式。

7、基础数据类型变量定义的注意事项

  • 初始化值必须在变量的取值范围内,如果超出,编译报错;

  • 定义初始化long类型变量,初始化值后面需要添加字母L或者l,推荐使用L;

  • 定义初始化float类型变量,初始化值后面需要添加字母F或者f;

  • 在方法中定义的变量,在使用前必须进行初始化;

  • 在同一作用域内定义的变量不可以同名;

    【作用域】:就是变量直接所属的那对大括号;

  • 变量的使用不能超出所属的作用域范围,否则编译报错;

  • 在定义初始化的变量时,可以先定义后初始化,推荐使用直接定义初始化方式;

  • 在定义初始化同一种数据类型多个变量时,可以通过一个数据类型在一行上直接定义多个变量,中 间用 逗号“,” 间隔

第二天

1、类型转换

类型转换
含义:数据类型之间的转换
分类:
基本类型间的转换
引用类型间的转换(暂不涉及)
基本类型和包装类型间的转换(暂不涉及)
基本类型和字符串类型间的转换(暂不涉及)

基本类型转换
含义:
将基本类型的数据进行类型转换
分类:
隐式转换(自动转换)
显式转换(强制转换)
格式:
数据类型A 变量名 = (数据类型A)数据类型B的数据值;
注意:
基本类型转换的格式适用于自动转换,也适用于强制转换,在自动转换时可以省略不写

自动转换(隐式转换)
含义:
将取值范围较小的数据类型转换成取值范围较大的数据类型
格式:
取值范围较大的数据类型 变量名 = (取值范围较大的数据类型)取值范围较小的数据类型的数据值
注意:
1.在自动转换时,可以将类型转换的格式省略不写
2.boolean不可以进行基本类型转换

	根据基本类型取值范围大小关系总结如下:
		byte < short < int < long < float < double
				char < int < long < float < double

面试题:
	在内存中每创建一个long类型变量,8个字节,每创建一个float类型变量,4个字节,为什么long的取值范围要比float的取值范围小?
		1.基本类型转换只和取值范围有关,和占用字节数无关
		2.long的取值范围中最大的整数是2^63-1,这个数小于2^63
		float的取值范围中最大的整数大于3.4*10^38
			3.4*10^38 > 2*10^38 > 2*8^38 = 2*(2^3)^38 = 2*2^114= 2^115
		综上所述:long的取值范围比float的取值范围小

强制转换(显式转换)
含义:
将取值范围较大的数据类型转换为取值范围较小的数据类型
格式:
取值范围较小的数据类型 变量名 = (取值范围较小的数据类型)取值范围较大数据类型的数据值;
注意:
尽量避免强制转换,因为可能会发生精度的损失和数据的溢出

基本类型转换的注意事项:
1.基本类型转换的格式适用于自动转换,也适用于强制转换,在自动转换时,格式可以省略不写;
2.基本类型转换是七种数值类型间的转换,与boolean类型无关
3.尽量避免强制转换,因为可能会发生精度的损失和数据的溢出
4.byte,short,char这三种数据类型只要参与数学运算,先将数据越级提升成int类型,再参与运算操作;如果没有参与数学运算,依然遵循取值范围的特点转换

2、常量

常量:
含义:在程序中其值不可以发生改变的量
分类:
字面值常量
自定义常量(暂不涉及,讲解final关键字时讲解)

字面值常量:
	含义:单独的一个值,不具备任何意义,需要结合其它语句进行使用
	分类:
		整数字面值常量
		浮点字面值常量
		字符字面值常量
		布尔字面值常量
		字符串字面值常量
		空字面值常量

常量的注意事项:
1.给byte,short,char类型变量进行赋值操作时,如果右边的初始化值是字面值常量,且还在该数据类型的取值范围内,JVM的编译器自动在编译时期将其优化为byte类型数据,我们将这一优化称之为"常量优化机制"

​ 2.给变量进行赋值操作时,如果右边的初始化值是一个数学运算式子,且运算的符号两边都是字面值常量和运算后的结果还在该数据类型的取值范围内,JVM的编译器会将这一运算过程由运行时期提前至编译时期,在生成.class文件已运算完毕,我们将这一优化称之为"常量优化机制"

3、两种常见的输出语句

​ 换行输出语句:
​ 格式:System.out.println(输出内容);
​ 特点:在控制台打印内容后,进行"回车换行"处理
​ 直接输出语句:
​ 格式:System.out.print(输出内容);
​ 特点:在控制台打印内容后,不做任何处理
​ 注意事项:
​ 换行输出语句如果里面没有任何内容,直接进行回车换行处理
​ 直接输出语句如果里面没有任何内容,编译报错

回车:将光标移动到该行的行首
换行:将贯标移动到下一行相同的位置

4、运算符

​ 含义:在程序中用来连接变量或常量的符号

表达式:
含义:在程序中用运算符连接起来的式子

常见运算符的分类
算术运算符
赋值运算符
关系运算符
逻辑运算符
三元运算符
位运算符

1.算术运算符

​ 含义:
​ 在程序中针对变量或常量进行运算操作的运算符
​ 分类:
​ 数学运算符

+, - , * , / , %(取余)
自增运算符
++
自减运算符

数学运算符的注意事项:
	/:取的是两个数相除的商
	%:取的是两个数相除的余数

需求:获取指定四位数中个位,十位,百位,千位上的数字

public static void main (String[] args) {
		//声明并初始化四位整数
		int num = 1234;

		//获取该四位数各位位上的数字
		int ge = num % 10;
		int shi = num / 10 % 10;
		int bai = num / 100 % 10;
		int qian = num / 1000 % 10;

		System.out.println("个位上的数字是:" + ge);
		System.out.println("十位上的数字是:" + shi);
		System.out.println("百位上的数字是:" + bai);
		System.out.println("千位上的数字是:" + qian);
	}

对于一个数(num)个十百千万的位数求法规律:

个位:num%10

十位:num/10%10

百位:num/100%10

千位:num/1000%10

万位:num/10000%10

…位:num/…%10

2.+号的使用

+号的多种用法:
1.加法运算符
2.字符串连接符

字符串连接符
含义:当+号两边有任何一边出现了字符串时,+号不再起到加法运算操作,而是转换成字符串连接符,将两边的内容进行连接操作,得到新的字符串

3.自增自减运算符

自增自减运算符(++,–)
自增运算符:
含义:将变量进行+1操作,再将结果赋值给该变量
格式:
变量名++
++变量名
特点:
单独使用:
前++和后++的运算结果是一样的,运算过程可以忽略不计
复合使用:
如果++在前,先自增,再使用
如果++在后,先使用,再自增
自减运算符:
含义:将变量进行-1操作,再将结果赋值给该变量
格式:
变量名–
–变量名
特点:
单独使用:
前–和后–的运算结果是一样的,运算过程可以忽略不计
复合使用:
如果–在前,先自减,再使用
如果–在后,先使用,再自减

4.赋值运算符

​ 含义:
​ 进行赋值操作的运算符
​ 分类:
​ 基础赋值运算符
​ =
​ 扩展赋值运算符
​ += , -= , *= , /= , %= , …
​ 含义:将运算符两边的内容进行运算操作,再将运算后的结果赋值给左边变量

	注意:
		扩展的赋值运算符再将结果赋值给左边的变量之前,会根据左边的变量的数据类型针对结果进行隐式的强制转换
5.关系运算符

​ 含义:
​ 针对变量进行比较的运算符
​ 分类:
​ < , <= , > , >= , == , !=
​ 注意:
​ 1.关系表达式的结果一定是boolean值
​ 2.在程序中不要将写成=,也不要将=写成

==的含义:
比较基本类型数据值是否相等

!=的含义:
比较基本类型数据值是否不等

6.逻辑运算符

​ 含义:
​ 用来连接boolean值结果的运算符
​ 分类:
​ 基础逻辑运算符
​ & , | , ^ , !
​ 短路逻辑运算符
​ && , ||

基础逻辑运算符
	运算符&:
		含义:
			与,且
		特点:
			有false则false
		场景:
			判断多个条件是否同时满足

	运算符|:
		含义:
			或
		特点:
			有true则true
		场景:
			判断多个条件是否至少满足一个

	运算符^:(了解)
		含义:
			异或
		特点:
			相同为false,不同为true
		场景:
			判断两个条件结果是否不等

	运算符!:
		含义:
			非
		特点:
			非false则true,非true则false
		场景:
			针对boolean值结果进行取反
7.短路逻辑运算符

​ && ||

	运算符&&:
		含义:
			双与,短路与
		特点:
			有false则false
		注意:
			&和&&的结果是一样的;&&具有短路效果,当运算符前面的表达式结果为false时,运算符后面的表达式不会被执行;如果是&的话,无论运算符前面的表达式结果是true还是false,运算符后面的表达式都会被执行,在实际应用中更推荐是用&&


	运算符||:
		含义:
			双或,短路或
		特点:
			有true则true
		注意:
			|和||的结果是一样的;||具有短路效果,当运算符前面的表达式结果为true时,运算符后面的表达式不会被执行;如果是|的话,无论运算符前面的表达式结果是true还是false,运算符后面的表达式都会被执行,在实际应用中更推荐是用||
8.三元运算符

​ 含义:含有三个未知量的运算符
​ 格式:
​ 关系表达式 ? 结果值1 : 结果值2;
​ 流程:
​ 1.先确认关系表达式的结果是true还是false
​ 2.在确认关系表达式结果的同时,统一结果值1和结果值2的数据类型
​ 3.如果是true,执行统一数据类型后的结果值1;
​ 如果是false,执行统一数据类型后的结果值2;

9.位运算符

​ 含义:针对二进制进行操作的运算符
​ 目的:
​ 1.查看源码
​ 2.面试题
​ 分类:
​ 按位运算符
​ & , | , ^ , ~
​ 移位运算符:
​ << , >> , >>>

按位运算符&:
	含义:
		按位与,当两位相同时为1时才返回1

按位运算符|:
	含义:
		按位或,只要有一位为1即可返回
		1

按位运算符^:
	含义:
		按位异或。当两位相同时返回0,不同时返回1

按位运算符~:
	含义:
		按位非,将操作数的每个位(包括符号位)全部取反
10.移位运算符<<

​ 含义:
​ 将指定数据的二进制往左移动指定的位数
​ 格式:
​ 指定数据 << 移动位数
​ 特点:
​ 将指定数据的二进制往左移动指定的位数,符号位也随之移动,如果低位出现了空位,需要补0进行占位

移位运算符>>:
	含义:
		将指定数据的二进制往右移动指定的位数
	格式:
		指定数据 >> 移动位数
	特点:
		将指定数据的二进制往右移动指定的位数,符号位也随之移动,如果高位出现了空位,需要补和符号位相同的数字进行占位

移位运算符>>>:
	含义:
		将指定数据的二进制往右移动指定的位数
	格式:
		指定数据 >>> 移动位数
	特点:
		将指定数据的二进制往右移动指定的位数,符号位也随之移动,如果高位出现了空位,需要补0进行占位

5、原码反码补码

​ 相同点:
​ 在程序中都是针对二进制数据进行定点表示法:
​ (1)原反补针对二进制规定了符号位,最高位即符号位,正数为0,负数为1
​ (2)原反补针对存储占用的字节数进行二进制补全,不足位数,在符号位后面进行补0操作
​ int类型的1:
​ 二进制1
​ 定点表示法:
​ 00000000 00000000 00000000 00000001
​ 不同点:
​ 原码:是屏幕显式数据的二进制定点表示法
​ 补码:计算机中所有的数据操作都拿原码的补码形式进行计算操作
​ 反码:针对原码和补码进行转换的中间量

数据存储操作过程:
	1.显式的数据==>数据的原码
		如果数据是正整数,符号位确认为0,并补足位数
		如果数据是负整数,符号位确认为1,并补足位数
	2.数据的原码==>数据的反码
		如果数据是正整数,数据的反码与其原码相同
		如果数据是负整数,数据的反码是针对原码进行逐位取反,符号位保持不变
	3.数据的反码==>数据的补码
		如果数据是正整数,数据的补码与其反码相同
		如果数据是负整数,数据的补码在其反码的基础上进行+1运算
	4.数据的补码==>结果的补码
		数据的存储或操作
	5.结果的补码==>结果的反码
		如果结果是正整数,结果的反码与其补码相同
		如果结果是负整数,结果的反码在其补码的基础上进行-1运算
	6.结果的反码==>结果的原码
		如果结果是正整数,结果的原码与其反码相同
		如果结果是负整数,结果的原码是针对反码进行逐位取反,符号位保持不变
	7.结果的原码==>显式的结果
		如果结果是正整数,直接转换显式结果
		如果结果是负整数,忽略符号位,将其直接转换显式结果并添加负号

将int类型的130强制转换成byte类型
	1.显式的数据==>数据的原码
		数据的原码:00000000 00000000 00000000 10000010
	2.数据的原码==>数据的反码
		数据的反码:00000000 00000000 00000000 10000010
	3.数据的反码==>数据的补码
		数据的补码:00000000 00000000 00000000 10000010
	4.数据的补码==>结果的补码
		结果的补码:10000010
	5.结果的补码==>结果的反码
		结果的反码:10000001
	6.结果的反码==>结果的原码
		结果的原码:11111110
	7.结果的原码==>显式的结果
		显式的结果:-126

第三天

1、流程语句与控制语句

1.流程语句

顺序结构:代码从上至下依次执行
流程控制作用于顺序结构中

流程控制:
	流程控制语句:
		分支结构:
			if语句
			switch语句
		循环结构
			for语句
			while语句
			dowhile语句
	流程控制代码块:
		调用结构:
			方法

学习流程控制的小技巧
1.记住流程控制的语法格式
2.记住流程控制的语法流程
3.精通流程控制的实际应用

三种循环的区别:
1.循环语句的执行次数(当循环条件语句为false时,循环体语句至少执行的次数)
for语句和while语句当循环条件语句为false时,循环体语句至少执行0次
do…while…语句当循环条件语句为false时,循环体语句至少执行1次
2.实际开发中如何选择
有明显的循环次数或循环范围,选择for语句
没有明显的循环次数或循环范围,选择while语句
在实际开发中不会选择do…while…语句

2.控制语句

​ 含义:操作流程控制的语句
​ 分类:
​ break跳出整个循环,结束
​ continue跳出当前循环进入到下一个循环
​ return(暂不涉及)

break关键字
	场景
		1.switch语句中
		2.循环语句中
	作用:
		1.结束switch语句
		2.结束循环语句

continue关键字:
	场景:
		循环语句中
	作用:
		跳出本次循环,继续下一次循环


控制语句的注意事项:
	在和控制语句相同的作用域内,控制语句的后面不可以编写任何代码,
	否则编译报错,因为永远执行不到

流程控制语句的注意事项:
if语句,for语句,while语句如果{}中的语句体有且仅有一行时,
{}可以省略不写,但是实际开发中推荐写上

1、dowhile语句的格式:

​ do {
​ 循环体语句
​ } while (循环条件语句);

执行流程:
	1.先执行循环体语句;
	2.再确认循环条件语句的结果是true还是false;
	3.如果是true,跳回第1步,继续执行;
	  如果是false,dowhile语句结束;

为了和for循环进行转换,衍生出dowhile语句的扩展格式
	初始化语句
	do {
		循环体语句
		迭代语句
	} while (循环条件语句);
2、for语句的基本格式:

​ for (初始化语句;循环条件语句;迭代语句) {
​ 循环体语句
​ }

	初始化语句:声明初始化循环变量的语句
	循环条件语句:循环的判断条件
	循环体语句:重复执行多次的代码
	迭代语句:循环的后续操作
执行流程:
	1.先执行初始化语句;
	2.确认循环条件语句的结果是true还是false;
	3.如果是true,执行循环体语句;
	  如果是false,for语句结束;
	4.执行迭代语句;
	5.跳回第2步,继续执行

练习:打印所有的水仙花数

什么是水仙花数呢?
	所谓的水仙花数是指一个三位数,其各位数字的立方和等于该数本身。
	举例:153就是一个水仙花数。
	153 = 1*1*1 + 5*5*5 + 3*3*3
public class ForDemo05 {
	public static void main (String[] args) {
		//遍历所有的三位数
		for (int i = 100 ; i < 1000 ; i++) {
			//获取每个三位数的个位,十位,百位上的数字
			int ge = i % 10;
			int shi = i / 10 % 10;
			int bai = i / 100 % 10;

			//进行数据的筛选
			if (i == bai*bai*bai + shi*shi*shi + ge*ge*ge) {
				System.out.println(i);
			}
		}
	}
}

分析以下需求,并用代码实现:
(1)打印出四位数字中个位+百位=十位+千位并且个位数为偶数,千位数为奇数的数字,并打印符合条件的数字的个数
(2)符合条件的数字,每行显示5个,用空格隔开,打印格式如下:
1012 1034 1056 1078 1100
1122 1144 1166 1188 1210
//…
符合条件的数字总共有: 165个

public class ForDemo07 {
	public static void main (String[] args) {
		//声明并初始化计数器变量
		int count = 0;

		//遍历所有的四位数
		for (int i = 1000; i < 10000 ; i++) {
			//获取每个四位数中的个位,十位,百位,千位
			int ge = i % 10;
			int shi = i / 10 % 10;
			int bai = i / 100 % 10;
			int qian = i / 1000 % 10;

			//进行数据的筛选
			if ((ge + bai == shi + qian) && (ge % 2 == 0) && (qian % 2 == 1)) {
				//计数器变量累加
				count++;

				//打印符合要求的数字
				if (count % 5 == 0) {
					System.out.println(i);
				} else {
					System.out.print(i + " ");
				}
			}
		}


		//打印计数器变量
		System.out.println("符合条件的数字总共有: " + count + "个");
	}
}
3、if语句格式

if语句的第一种格式
if (条件判断语句) {
语句体
}

执行流程:
	1.先确认条件判断语句的结果是true还是false;
	2.如果是true,执行语句体,if语句结束;
	  如果是false,if语句语句;

if语句的第二种格式:
if (条件判断语句) {
语句体1
} else {
语句体2
}

执行流程:
	1.先看条件判断语句的结果是true还是false;
	2.如果是true,执行语句体1;
	  如果是false,执行语句体2;
注意事项:
	if语句的语句体可以是多行逻辑代码,三元运算符只能是结果值,具有一定的局限性,在实际开发中更推荐使用if语句的第二种格式

if语句的第三种格式:
if (条件判断语句1) {
语句体1
} else if (条件判断语句2) {
语句体2
}

else if (条件判断语句n) {
语句体n
} else {
语句体n+1
}

执行流程:
	1.先确认条件判断语句1中的结果是true还是false
	2.如果是true,执行语句体1,if语句结束;
	  如果是false,确认条件判断语句2中的结果是true还是false
	......
	3.当所有条件判断语句的结果都是false,执行else中的语句体n+1

注意:
	if语句的第三种格式,else语句在部分场景中可以省略不写,但是推荐写上

练习

x和y的关系满足如下:
x>=3 y = 2x + 1;
-1 x<=-1 y = 2x - 1;
根据给定的x的值,计算出y的值并输出。

public static void main (String[] args) {
		//声明并初始化x变量
		int x = 5;

		//声明y变量
		int y;

		//针对x进行判断
		if (x >= 3) {
			y = 2 * x + 1;
		} else if (x > -1 && x < 3) {
			y = 2 * x;
		} else {
			y = 2 * x - 1;
		}

		//打印y的值
		System.out.println("y = " + y);
	}
4、switch语句的基本格式:

​ switch (选择值) {
​ case 数据值1:
​ 语句体1
​ break;
​ case 数据值2:
​ 语句体2
​ break;
​ …
​ case 数据值n:
​ 语句体n
​ break;
​ default:
​ 语句体n+1
​ break;
​ }

执行流程:
	1.先确认选择值的最终结果是多少
	2.选择数据值1和选择值进行匹配,看是否匹配成功;
	3.如果匹配成功,执行语句体1,执行break,switch语句结束;
	  如果匹配失败,选择数据值2和选择值进行匹配,看是否匹配成功;
	......
	4.当所有的数据值和选择值都匹配失败,执行default中的语句体n+1,执行break,switch语句结束;

switch语句的注意事项:
1.switch语句()中选择值的数据类型只支持以下几种:
基本类型:原则上在内存中只支持int类型,因为自动转换可以衍生出byte,short,char
引用类型:
JDK5.0(包含)以后:支持枚举Enum类型
JDK7.0(包含)以后:支持字符串String类型
2.swtich语句中的default和if语句的第三种格式中else类似,可以省略不写,推荐写上
3.swtich语句中的default和case位置可以互换,但不影响执行流程
4.switch语句中的break可以省略不写,但是会出现case穿透效果

根据指定的月份输出对应季节
一年有四季`````
3,4,5 春季
6,7,8 夏季
9,10,11 秋季
12,1,2 冬季

if语句和switch语句的区别:
if语句可以针对范围或条件进行判断,但switch语句不能,在实际应用中更推荐使用if语句,在底层中switch语句的执行效率比if语句高,但是在硬件过剩的今天,这点效率可以忽略不计

public class SwitchDemo03 {
	public static void main (String[] args) {
		//声明并初始化月份变量
		int month = 5;

		if (month >= 1 && month <= 12) {
			if (month == 1) {
				System.out.println("冬季");
			} else if (month == 2) {
				System.out.println("冬季");
			} else if (month == 3) {
				System.out.println("春季");
			} else if (month == 4) {
				System.out.println("春季");
			} else if (month == 5) {
				System.out.println("春季");
			} else if (month == 6) {
				System.out.println("夏季");
			} else if (month == 7) {
				System.out.println("夏季");
			} else if (month == 8) {
				System.out.println("夏季");
			} else if (month == 9) {
				System.out.println("秋季");
			} else if (month == 10) {
				System.out.println("秋季");
			} else if (month == 11) {
				System.out.println("秋季");
			} else {
				System.out.println("冬季");
			}
		} else {
			System.out.println("月份有误");
		}

		System.out.println("================================");

		if (month >= 1 && month <= 12) {
			if (month == 3 || month == 4 || month == 5) {
				System.out.println("春季");
			} else if (month == 6 || month == 7 || month == 8) {
				System.out.println("夏季");
			} else if (month == 9 || month == 10 || month == 11) {
				System.out.println("秋季");
			} else {
				System.out.println("冬季");
			}
		} else {
			System.out.println("月份有误");
		}

		System.out.println("================================");

		switch (month) {
			case 1:
				System.out.println("冬季");
				break;
			case 2:
				System.out.println("冬季");
				break;
			case 3:
				System.out.println("春季");
				break;
			case 4:
				System.out.println("春季");
				break;
			case 5:
				System.out.println("春季");
				break;
			case 6:
				System.out.println("夏季");
				break;
			case 7:
				System.out.println("夏季");
				break;
			case 8:
				System.out.println("夏季");
				break;
			case 9:
				System.out.println("秋季");
				break;
			case 10:
				System.out.println("秋季");
				break;
			case 11:
				System.out.println("秋季");
				break;
			case 12:
				System.out.println("冬季");
				break;
			default:
				System.out.println("月份有误");
				break;
		}

		System.out.println("================================");

		switch (month) {



			case 3:
			case 4:
			case 5:
				System.out.println("春季");
				break;

			case 6:
			case 7:
			case 8:
				System.out.println("夏季");
				break;

			case 9:
			case 10:
			case 11:
				System.out.println("秋季");
				break;

			case 1:
			case 2:
			case 12:
				System.out.println("冬季");
				break;

			default:
				System.out.println("月份有误");
				break;
		}
	}
}
5、while循环的格式:

​ while (循环条件语句) {
​ 循环体语句
​ }

执行流程:
	1.先确认循环条件语句的结果是true还是false;
	2.如果是true,执行循环体语句;
	  如果是false,while语句结束;
	3.跳回第1步,继续执行

为了和for语句之间进行转换,衍生出while循环扩展格式:

	初始化语句
	while (循环条件语句) {
		循环体语句
		迭代语句
	}

练习:趣味折纸

题目:
世界最高山峰是珠穆朗玛峰,它的高度是8848.86米,假如我有一张足够大的纸,它的厚度是0.1毫米。请问,折叠多少次,不低于珠穆朗玛峰的高度?

public class WhileDemo02 {
	public static void main (String[] args) {
		//声明并初始化折纸的次数计算器变量
		int count = 0;

		//声明并初始化珠峰高度变量和纸厚度变量
		int zf = 88488600;
		int zhi = 1;

		//考虑到没有明显的循环次数,选择while循环
		while (zhi < zf) {
			//计数器累加
			count++;

			//进行折纸
			zhi *= 2;
		}

		System.out.println("count = " + count);
	}
}

2、运算符优先级的注意事项

​ 1.Java中运算符的优先级和数学中的优先级不一样,不是谁优先级高,就最先执行谁,而是式子从左到右依次执行,
​ 遇到运算符的时候,再考虑优先级问题,先执行谁后执行是谁的问题
​ 2.不要把一个表达式写得过于复杂,如果一个表达式过于复杂,则把它分成几步来完成;
​ 3.不要过多的依赖运算的优先级来控制表达式的执行顺序,这样可读性太差,尽量使用()来控制表达式的执行顺序
​ 4.如果表达式中出现了自增自减运算符,并且出现了(),这个时候需要特殊注意优先级问题
​ 5.如果表达式中出现了自增自减运算符,且=的两边出现了相同的变量,这个时候需要特殊注意优先级问题
​ 6.如果表达式中出现了扩展的赋值运算符,需要将扩展的赋值运算符拆分成基础赋值运算符再进行计算
​ 7.除了上述情况外,不需要特殊关注运算符优先级问题

第一天(day04–5.31)

1、方法(不调用,不执行)

1.方法的定义

定义方法的两个明确:方法返回值类型和参数列表

​ 格式:

修饰符 返回值类型 方法名 (参数1数据类型 参数1名,…){

​ 方法体语句

​ return 返回值;

}

修饰符:用来限制方法的关键字,public static(目前固定)

返回值类型:用来接收方法结果的数据类型

​ 基本数据类型(四类八种)、引用数据类型(数组、类、接口)

​ 无参的返回值类型:void

​ 使用void时,方法体中的return可以省略

方法执行有两个关键因素:传参和返回

传参:将实参传递给形参的过程(自定义方法要调用方法中的数据时,需要通过传参进行数据传递)

​ 实参(实际参数):调用方法时()中的参数,实参是具体的数据值或地址值
​ 形参(形式参数):声明方法时()中的参数,形参是声明变量类型和变量名

返回:调用自定义方法中的数据时,将自定义方法结果返回给调用方法的过程

2.方法的调用

3种方式:直接调用、输出调用、赋值调用

直接调用:同一类中,直接在main方法中输入要调用方法的方法名。

			在没有返回值类型(void)的方法中,只能通过直接调用方法

输出调用:通过输出语句进行方法的调用‘

赋值调用:数据类型 变量名=方法名(参数);

​ 在有返回值类型的情况下,方法的调用都可以使用,推荐使用赋值调用。

3.重载

同一类中(或子父类继承关系中)方法名一样,参数列表不一样

参数列表的不一样指的是:
1、参数数据类型不一样

​ 2、参数的数量不一样

​ 3、参数数据类型的顺序不同

与参数名无关,与参数方法的返回值类型无关

2、递归

方法自己调用自己形成的循环现象

直接递归
方法A中调用方法A
间接递归
方法A中调用方法B,方法B中调用方法C,方法C中调用方法A

递归一定要有条件限定,保证递归能够停止下来,否则会发生栈内存溢出。

在递归中虽然有限定条件,但是递归次数不能太多。否则也会发生栈内存溢出。

3、for循环嵌套

for (外层循环的初始化语句;外层循环的循环条件语句;外层循环的迭代语句) { 
    for (内层循环的初始化语句;内层循环的循环条件语句;内层循环的迭代语句) { 
        内层循环的循环体语句; 
   }
}

4、死循环

死循环:在程序中无法通过靠自身控制终止的循环

分类:

for语句的死循环

while语句的死循环

for语句的死循环格式

for (;;) { 

循环体语句; 

}

while语句的死循环格式

while (true) { 

循环体语句; 
}

第二天(day05–6.1)

IDEA的层次结构

1、数组
  1. 数组是一种引用数据类型
  2. 数组当中的多个数据必须为同一种数据类型(数组中的元素支持变量的类型转换,保证内存中是同一种数据类型即可)
  3. 数组的长度一旦初始化,长度就不可改变
  4. 静态初始化的简化版不可以先声明后初始化
  • 原因:静态初始化简化版直接声明初始化时,是根据数组名前面的数据类型[]在内存中申请并开辟内存空间,new 数据类型[]由JVM隐式进行提供;静态初始化简化版如果先声明后初始化,在初始化过程中无法直接获取数组名前面的数据类型[],JVM不会隐式提供new 数据类型[]
    • 静态数组初始化简化版在声明初始化时,java虚拟机(jvm)会根据数组名前的数据类型[] 在内存中申请并开辟内存空间,new 数据类型[]是由jvm隐式提供的;
    • 而先声明再初始化时,无法直接获取数组名前面的数据类型[],jvm不会隐式提供new 数据类型[]

1.数组的声明(定义)

​ 数据类型[] 数组名;

​ 数据类型 数组名[];

2.数组的初始化

在内存中创建一个数组,并其中赋予一些默认值

1)静态初始化(只初始化数组中的元素,不初始化数组的长度)

​ 数据类型[] 数组名=new 数据类型[]{数值1,数值2,…};

​ 数据类型[] 数组名={};【简化版】

通过数组静态初始化的简化版创建数组时,JVM会根据数组的数据类型在内存中申请并创建数组,new 数据类型[]由JVM在创建数组时隐式填充

数组静态初始化内存图解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BiqdCIKF-1658415050638)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\1654242792327.png)]

2)动态初始化(只初始化数组的长度,不初始化数组的元素)

​ 数据类型[] 数组名=new 数据类型[数组长度];

数组的索引(索引值都是从0开始的,数组的输出通过“数组名[索引值]”),数组当中的最大的地址值为数组长度-1

​ 获取数组中元素时,不能访问不存在的索引或错误索引,否则会发生索引越界异常(ArrayIndexOutOfBoundsException)

数组长度的动态获取:数组名.length动态获取数组长度

数组动态初始化内存图解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vl3bi59m-1658415050638)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\1654242687953.png)]

两个数组指向同一个地址值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DkyJqNxM-1658415050640)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\1654242816997.png)]

2、java的内存划分

JDK6.0(包含)以前

  • 寄存器(程序计数器):存储和计算机硬件(CPU)相关的内容
  • 本地栈内存:存储和操作系统相关的内容
  • 本地方法栈:俗称"栈内存",正在运行的方法
  • 堆内存:存储隐式和显式new出来的东西(凡是new出来的东西都在堆内存中)
  • 方法区:存储字节码文件对象(类文件Class)

JDK7.0

  • 寄存器(程序计数器):存储和计算机硬件(CPU)相关的内容
  • 本地栈内存:存储和操作系统相关的内容
  • 本地方法栈:俗称"栈内存",正在运行的方法
  • 堆内存:存储隐式和显式new出来的东西
  • 将方法区划归堆内存中

JDK8.0(包含)以后

  • 寄存器(程序计数器):存储和计算机硬件(CPU)相关的内容
  • 本地栈内存:存储和操作系统相关的内容
  • 本地方法栈:俗称"栈内存",正在运行的方法
  • 堆内存:存储隐式和显式new出来的东西
  • 元空间:存储特殊值的空间

栈内存(本地栈内存):存储“正在运行的方法”的内存

1.方法中的内容会随着方法的进栈而进栈,会随着方法出栈而消失

2.方法的出栈是指在栈内存中立刻消失

3.方法进栈和出栈遵循"先进后出,后进先出"原则

方法区的特点:

含义:

所应用到的字节码文件对象

包含:

静态区和常量区

特点:

方法区内部已有的数据,在二次获取时不会创建新的内容,会拿已有数据进行使用

堆内存的特点:

含义:

存储显式或隐式new出来的内容

特点:

1.堆内存中的每块区域都有独立的地址值:16进制

2.堆内存中每块区域中的数据都有默认值

  • 整数型 0
  • 浮点型 0.0
  • 字符型 空白字符 ‘\u 0000’
  • 布尔型 false
  • 引用型 null

3.在堆内存中有一个特殊的对象,该区域存储的是"垃圾回收器"

作用:

垃圾回收器类似于生活中的"扫地机器人",有规则的扫描堆内存,遇到垃圾数据进行回收

4.堆内存中每块区域在内存中消失的特点

该区域和其它内存区域没有任何的关联性时,该区域会被JVM标记为"垃圾数据",等待垃圾回收器的回收,垃圾回收器扫描到该区域时进行回收

数组的应用:

  • 基本应用:不会针对数组中元素的索引位置进行任何改动的应用
  • ​ 举例:求和,求最值,…
  • 高级应用:针对数组中元素的索引位置进行改变
  • ​ 举例:反转,排序
  • 综合应用:和之前讲解的方法进行综合使用
  • ​ 举例:数组的动态扩容,动态删除,动态插入

第三天(day06–6.2)【6.10】

1、冒泡排序

原理:获取最大值排序,将最大值依次移动到数组最右侧

int[] arr = {77, 44, 33, 66, 99, 66};
        for (int count = 1; count < arr.length; count++) {
            for (int i = 0; i < arr.length - count; i++) {
                if (arr[i] >= arr[i + 1]) {
                    int temp = arr[i + 1];
                    arr[i + 1] = arr[i];
                    arr[i] = temp;
                }
            }
        }
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }

进阶版冒泡排序

int[] arr = {77, 44, 33, 66, 99, 66};
        for (int count = 1; count < arr.length; count++) {
            int maxIndex = arr.length - count;
            for (int i = 0; i < arr.length - count; i++) {
                if (arr[i] > arr[maxIndex]) {
                    maxIndex = i;
                }
            }
        if (maxIndex != arr.length - count) {
            int temp = arr[maxIndex];
            arr[maxIndex] = arr[arr.length - count];
            arr[arr.length - count] = temp;
            }
        }
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
2、数组反转

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6AVVSdcA-1658415050641)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\1654237292839.png)]

//数组的反转
        //int[] arr=new int[]{1,2,3,4,5,6};
        int[] arr={1,2,3,4,5,6,7};
        int left=0;
        int right=arr.length-1;
        for (int i = 0; i < arr.length; i++) {
            if(left<right){
                int temp=arr[left];
                arr[left]=arr[right];
                arr[right]=temp;
                left++;
                right--;
            }
            System.out.println(arr[i]);
        }
3、方法进行传参的特点

​ 如果参数是基本数据类型:

​ 1.进行参数传递的是基本类型的数据值

2.形参数据值的改变不会影响实际的参数数据值

​ 如果是引用数据类型:

​ 1.进行参数传递的是引用类型的地址值

​ 2.形参地址值的改变不会影响实参的地址值

3.形参地址值不改变,形参地址值中的元素值的改变影响实参地址值中的元素值

原因:在创建数组时,new出来的东西都在堆内存中,在初始化数组长度后,堆内存中会为数组开辟对应的数组空间(对应数组长度,包含索引和默认元素),并赋数组元素默认值为0,通过数组创建时的元素将默认值进行覆盖。元素值的改变会影响堆内存中数组的元素值(会被覆盖)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NyBZqL2X-1658415050642)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\1654242279661.png)]

4、可变参数(jdk5.0)

在程序中以实参为元素进行静态初始化的数组

格式:

​ 数据类型… 可变参数名(int… arr)

可变参数的注意事项:

​ 1.当声明方法时,形参列表中除了可变参数外还存在其它参数,需要将可变参数写在形参列表中的最后一个位置

​ 2.当声明方法时,形参列表中的可变参数最多只能有一个

5、数组的折半查找(二分查找)

前提:数组一定要是一个有序的。

折半查找图解:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C8dHZTkx-1658415050644)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\1654244451022.png)]

left指针指向最左边元素,right指向最右边元素,mid指向的是left和right的中间元素{mid=(left+right)/2}

当要查找的元素小于mid时,将right指向mid-1,mid=(left+right)/2;如果要查找元素大于mid时,将left指向mid+1,mid=(left+right)/2;

 int[] arr = {2,5,7,8,10,15,18,15,20,22,25,28};
        int left=0;
        int right=arr.length-1;
        int mid=(left+right)/2;
        int index=-1;
        for(int i=0;i<arr.length;i++){
            if(arr[mid]>num){
                right--;
            }else if(arr[mid]<num){
                left++;
            }else{
                index=mid;
            }
            mid=(left+right)/2;
        }
        if(index==-1){
            System.out.println("没找到");
        }
        System.out.println("第一次出现的位置:"+index);
    }
6、数组的动态扩容、插入、删除

扩容:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P4echvBW-1658415050644)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\1654308871691.png)]

private static void demo5(int num) {
        int[] oldArr={11,22,33};
        //根据旧数组创建新数组
        int[] newArr=new int[oldArr.length+1];
        //迁移之前的元素
        for (int i = 0; i < oldArr.length; i++) {
            newArr[i]=oldArr[i];
        }
        newArr[newArr.length-1]=num;
        System.out.println("扩容前:");
        for (int i = 0; i < oldArr.length; i++) {
            System.out.print(oldArr[i]+" ");
        }
        System.out.println("扩容后:");
        for (int i = 0; i < newArr.length; i++) {
            System.out.print(newArr[i]+" ");
        }

    }

插入:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xJj1IGao-1658415050646)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\1654308822553.png)]

 private static void demo6(int index,int num) {
        int[] oldArr={11,22,33};
        if(index<0||index>=oldArr.length){
            System.out.println("数组越界异常");
        }
        if(oldArr.length==0){
            System.out.println("数组长度为零");
        }
        int[] newArr=new int[oldArr.length+1];
        for (int i = 0; i < oldArr.length; i++) {
            if(i<index){
                //等位置迁移
                newArr[i]=oldArr[i];
                newArr[index]=num;
            }
            if(i>=index){
                newArr[i+1]=oldArr[i];
                newArr[index]=num;
            }

        }
        System.out.println("插入前");
        for (int i = 0; i < oldArr.length; i++) {
            System.out.print(oldArr[i]+" ");
        }
        System.out.println();
        System.out.println("插入后");
        for (int i = 0; i < newArr.length; i++) {
            System.out.print(newArr[i]+" ");
        }

    }

删除:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AXhr3Fnm-1658415050647)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\1654310214814.png)]

 private static void demo7(int index) {
        int[] oldArr={11,22,33};
        System.out.println(oldArr);
        if(index<0||index>=oldArr.length){
            System.out.println("数组越界异常");
        }
        if(oldArr.length==0){
            System.out.println("数组长度为零");
        }
        int[] newArr=new int[oldArr.length-1];
        for (int i = 0; i < newArr.length; i++) {
            //遍历次数:i=0、i=1
            if(i<index){
                newArr[i]=oldArr[i];
            }
            if(i>=index){
                newArr[i]=oldArr[i+1];
            }
            System.out.println(newArr[i]+"  ");
        }

    }
7、二维数组

元素为一维数组的一个数组

格式1:动态初始化

数据类型[][] 数组名 = new 数据类型[m][n]; 

​ m:表示这个二维数组有多少个一维数组。

​ n:表示每一个一维数组的元素有多少个。

//创建了有m个的一维数组,每个一维数组里面有n个元素

注意:

以下格式也可以表示二维数组(不推荐)

数据类型 数组名[][] = new 数据类型[m][n]; 

数据类型[] 数组名[] = new 数据类型[m][n]; 

格式2:

数据类型[][] 数组名 = new 数据类型[m][]; 

​ m:表示这个二维数组有多少个一维数组。

​ 列数没有给出,可以动态的给。这一次是一个变化的列数。

*思考:

格式1int[][] arr=new int[2][3];
System.out.println(arr[0]);
//[I@28d93b30
格式2int[][] arr=new int[2][];
System.out.println(arr[0]);
//null

为什么使用格式一打印输出地址值,而格式二却是null

格式3:

基本格式:

数据类型[][] 数组名 = new 数据类型[][]{{元素1,元素2...},{元素1,元素2...},{元素1,元素 

2...}}; 

简化版格式:

数据类型[][] 数组名 = {{元素1,元素2...},{元素1,元素2...},{元素1,元素2...}}; 

举例:

int[][] arr = {{1,2,3},{4,5,6},{7,8,9}}; 

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

二维数组的遍历

int[][] arr={{11,22,33},{44,55},{66,77,88}};
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr[i].length; j++) {
                System.out.println(arr[i][j]);
            }
        }

第四天(day07–6.6)

1、什么是面向对象

1.面向对象和面向过程区别

1)面向过程思想:

​ 代表:C语言

​ 单位:函数

​ 特点:比喻为"执行者"身份,偏重事情"怎么做",强调的是"过程"

2)面向对象思想:

​ 代表:Java语言

​ 单位:类文件(.class文件或Class对象)

​ 特点:比喻"指挥者"身份,偏重事情"找谁做",强调的是"对象"

​ 面向过程往往解决的是一些较小的问题

​ 面向对象往往解决的是一些较大的问题

面向过程和面向对象没有好坏之分,都是人们在生活中解决问题的手段,在不同的情况使用不同的解决方案

2.面向对象思想三个阶段:

​ 面向对象基础思想(Java基础阶段)

​ 面向接口编程思想(JDBC,web阶段)

​ 面向切面编程思想(Spring框架)

3.概述:

面向对象的方法主要是把事物给对象化,包括其属性和行为。总体来说面向对象的底层还是面向过程,面向过程抽象成类,然后封装,方便使用就是面向对象。

面向对象思想就是在计算机程序设计过程中,参照现实中事物,将事物的属性特征、行为特征抽象出来,描述成计算机事件的设计思 想。(万物皆对象)。

4.面向对象三大特征:

面向对象三大特征:封装继承多态

封装: 指将某事物的属性和行为包装到对象中,这个对象只对外公布需要公开的属性和行为,而这个公布也是可以有选择性的公布给其它对象。在java中能使用private、protected、public三种修饰符或不用(即默认defalut)对外部对象访问该对象的属性和行为进行限制。

继承: 子类继承父类,表明子类是一种特殊的父类,并且具有父类所不具有的 一些属性或方法。

多态: 不同事物具有不同表现形式的能力,相当于对于同一个接口类型,由多种不同的实现方式

2、类和对象

:是一组相关属性和行为的集合。可以看成是一类事物的模板,使用事物的属性特征和行为特征

来描述该类事物。

属性:就是该事物的状态信息。

行为:就是该事物能够做什么。

对象:是一类事物的具体体现。对象是类的一个实例,必然具备该类事物的属性和行为

类和对象的关系:

​ 类是对一类事物的描述,是抽象的

​ 对象是一类事物的实例,是具体的

类是对象的(抽象/)模板,对象是类的实体或实例

1.类的定义

定义格式

public class 类名 { 

//实例变量 :对应事物的属性

//实例方法 :对应事物的行为

} 
public class Student { 
    //实例变量 
    String name; 
    int age; 
    //实例方法 
    public void study () { 
        System.out.println(name + "good good study , day day up !!!"); 
    }
}
1.类的成员和内容

类的成员:

​ 归属于"类"或"对象"的内容

包含:

成员量(类的属性)-------->成员变量和成员常量

成员方法(类的行为)

成员内部类(暂不涉及)

​ 类的内容:

​ 不归属"类"或"对象"的内容

​ 包含:

​ 构造器

1、成员量:在程序中,成员变量和成员常量的统称

​ 1)成员变量:在程序中声明在类中方法外,且没有final关键字进行修饰的变量

​ ①实例变量:在程序中,声明在类中方法外,且没有static关键字修饰的成员变量,实例变量归属"对象"(对象变量)

​ ②静态变量:在程序中,声明在类中方法外,且含有static关键字修饰的成员变量,静态变量归属"类"(类变量,全局变量)

2)成员常量:在程序中声明在类中方法外,且含有final关键字进行修饰的变量(暂不涉及)

​ ①实例常量:在程序中,声明在类中方法外,且没有static关键字修饰的成员常量,实例常量归属"对象"(对象常量)

​ ②静态常量:在程序中,声明在类中方法外,且含有static关键字修饰的成员常量,静态常量归属"类"(类常量,全局常量)

2、成员方法

在程序中,声明在类中的方法

​ 1)实例方法:在程序中声明在类中没有static关键字修饰的成员方法,实例方法归属"对象"(对象方法)

​ 2)静态方法:在程序中声明在类中含有static关键字修饰的成员方法,静态方法归属"类"(类方法)

2.对象的定义

创建对象

类名 对象名 = new 类名();

使用对象访问类中的成员:

获取属性格式:

	对象名.实例变量名;

赋值属性格式:

	对象名.实例变量名 = 初始化值;

调用实例方法格式:

	对象名.实例方法名(实参);

	System.out.println(对象名.实例方法名(实参));

	数据类型 变量名 = 对象名.实例方法名(实参);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mCyYGEc0-1658415050649)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\1654513013646.png)]

3、实例(成员)变量和局部变量区别

方法的参数是局部变量,参数在方法调用时,必须会被赋值。

1.代码中的位置不同:

​ 实例变量:类中,方法外

​ 局部变量:方法内部或者方法声明上

2.内存中的位置不同

​ 实例变量:堆内存

​ 局部变量:栈内存

3.默认值不同

​ 实例变量:有默认值

​ 局部变量:没有默认值

4.代码中的作用域不同

​ 实例变量:类中

​ 局部变量:方法中

5.内存中的生命周期不同

​ 实例变量:随着对象的创建而加载,随着对象的回收而消失

​ 局部变量:随着方法的调用而加载,随着方法的出栈而消失

6.变量的加载方式和次数不同

​ 实例变量:随着对象的创建而加载,每创建一次就加载一次

​ 局部变量:随着方法的调用而加载,每调用一次就加载一次

7.修饰符不同

 实例变量:可以添加修饰符

 局部变量:只能被final关键字修饰

4、this关键字的用法

1.应用场景

(子)类的构造器中

(子)类的实例方法中

作用:用来区分同名的实例变量和局部变量

当方法的局部变量和类的成员变量重名的时候,根据“就近原则”优先使用局部变量。如果需要访问本类中的成员变量,则需要:

​ this.成员变量名

​ 哪个对象调用了this关键字所在的实例方法或者构造器,this就代表哪个对象(通过谁调用的方法,谁就是this)

格式:

获取实例变量 

		this.实例变量名 

获取实例方法 

		this.实例方法名(); 

this关键字的注意事项:

​ 在类中使用所有的实例变量或调用所有的实例方法,都有隐式的this进行修饰

2.应用场景

(子)类的构造器中

作用:当构造器自己无法进行实例初始化的时候,调用本类中其它的构造器完成实例初始化

格式:

​ this(实参)

注意事项:

​ this()必须写在构造器第一行

5、封装

封装其实就是给程序中的属性信息,行为动作等加上不同的访问权限修饰符,以便控制程序的安全性。它是指将对象的属性信息隐藏在对象内部,不允许外部程序直接访问对象内部属性信息,而是通过该类所提供的方法来实现对内部属性信息的操作和修改。

1.封装性的体现:

​ 1)方法就是一种封装

​ 将一些细节信息隐藏起来,对外界不可见

​ 2)private关键字也是一种封装

​ 一旦使用private关键字,本类中可以随意进行访问,但是超出本类范围之外就不能直接访问(需要通过getter/setter方法,间接访问private成员变量)

​ 对于getter,不能有参数,返回值类型和成员变量对应

​ 对于setter,不能有返回值,参数类型和成员变量对应

setXxx/getXxx命名规则。

对于基本类型中的boolean值,getter方法一定要写成isXxx的形式,setXxx不变。

2.修饰符

封装思想的核心就是给程序中不同的内容添加上不同级别的访问权限修饰符

访问权限级别由小到大依次是:

​ private(当前类访问权限)

​ (default)(包访问权限)

​ protected(子类访问权限)

​ public(公共访问权限)

private< (default)< protected< public
同一个类(自己) Y Y Y Y
同一个包(邻居) N Y Y Y
不同包子类(儿子) N N Y Y
不同包非子类(陌生人) N N N Y
3.封装的步骤
  1. 使用 private 关键字来修饰实例变量。
  2. 对需要访问的实例变量,提供对应的一对 getXxx 方法 、 setXxx 方法

6、构造方法(构造器)

专门用来创建对象的方法,当我们使用new关键字来创建对象时其实就是调用构造方法。

Java类必须包含一个或 多个构造方法(如果不写,默认有一个无参的构造方法。但是一旦编写至少一个构造方法,默认就不会有了)。

格式:

//无参构造方法

public 类名(){

}

//有参构造器

​ public 类名称(参数类型 参数名){

​ 方法体;

}

注意事项:

​ 1)当一个类没有任何显式的构造器的时候,JVM会自动为其提供一个默认无参的构造器;但是当这个 类一旦有任意一个显式的构造器时,JVM不在为其提供 。

​ 2)构造器是我们学习类中的第三个成员(其它两个为成员变量和成员方法),格式和语法上虽然和方法 比较相像,但构造器并不是方法

​ 3)构造器的名字和所在类名相同

​ 4)构造器不是方法,所以没有返回值类型,而且连void关键字都不能有

​ 5)构造器是可以重载的(方法名称相同,参数列表不同)

6)接口中没有构造器(暂不涉及)

​ 7)构造器中的{}有很多隐式的代码,执行的时候遵循优先级(实例初始化过程,暂不涉及)

​ 第一优先级:隐式或显式的super(实参),显式的this(实参)

​ 第二优先级:实例成员和构造器代码块

​ 第三优先级:构造器中除了第一优先级的显式代码

构造器代码块在new对象时候都会被调用一次

构造器代码块中的内容总是优先于构造器中的显式内容执行

7、JavaBean标准类

JavaBean 是 Java语言编写类的一种标准规范。符合 JavaBean 的类,要求类必须是具体的和公共的, 并且具有无参数的构造器,提供用来操作实例变量的 set 和 get 方法。

public class ClassName{ 
//私有化实例变量 【必须】 
//无参构造器【必须】 
//实例方法 
//getXxx() 【必须】 
//setXxx()【必须】 
//有参构造器【可以有】
//构造器代码块【可以有】
//静态代码块【可以有】
//成员内部类【可以有】
//toString(),equals(),hashCode()【可以有】
}

8、补充

1.匿名对象

在调用方法时,只能调用唯一 一次

public class OOPDemo03 { public static void main (String[] args) { 
    Student s = new Student();//s就是对象的名字,后续还需要使用对象,可以使用s, 不过因为堆内存中Student对象和栈内存中变量s具有指向性,s作为主方法的局部变量,JVM会在主方法 结束后将堆内存中的Student对象当做垃圾数据 
     new Student();//匿名对象,只允许被使用一次,因为和栈内存没有任何指向性,在使用 完毕后马上就被当做垃圾数据 
} 
                       }
2.私有方法

固定私有就是被private关键字修饰的方法,这样的方法不可以被外界所访问,只在本类中有效

格式

 private 返回值类型 方法名 () {}

调用方法:
    对象名.方法名()

作用:用来抽取多个方法中相同的内容,抽取后的方法又不想被外界所访问

3.构造代码块

用来处理构造器中有相同代码,可以通过构造代码块进行处理,创建对象后它会在构造方法显示代码前执行。

public class ConstructorCodeBlock {
    public ConstructorCodeBlock() {
        System.out.println("彤彤老师好");
    }

    public ConstructorCodeBlock(int num) {
        System.out.println("沙沙老师好");
    }

    {
        System.out.println("你好");
        System.out.println("我好");
        System.out.println("大家好");
    }
    
    

位置:

​ 类中方法外

作用:

​ 抽取所有构造器中相同的代码,并在构造器显式代码之前执行

格式:

​ {

​ 所有构造器中相同的代码

​ }

注意:

​ 构造器代码块由构造器中的隐式代码进行调用

(day08)(6.7)

1、static关键字

static关键字一旦使用,那么这样的内容就不再属于对象自己而是属于类的,所以凡是本类的对象,都共享一份。

一旦使用static关键字修饰成员方法,那么这个方法就是静态方法,静态方法不属于对象而是属于类。 调用格式:

	类名.静态方法名();【推荐】
或
    对象.静态方法名();

修饰:成员变量,成员方法,成员内部类,代码块

1.类变量(静态变量)

当 static 修饰成员变量时,该变量称为类变量。该类的每个对象都共享同一个类变量的值。

格式:

修饰符 static 数据类型 变量名; 
1)三种变量的区别(静态变量,实例变量,局部变量)

(1)代码中的位置不同:

​ 静态变量:类中方法外

​ 实例变量:类中方法外

​ 局部变量:方法内部或方法声明上

(2)内存中的位置不同:

​ 静态变量:堆内存

​ 实例变量:堆内存

​ 局部变量:栈内存

(3)默认值不同:

​ 静态变量:含有默认值

​ 实例变量:含有默认值

​ 局部变量:没有默认值

(4)代码中作用域不同:

​ 静态变量:所属类中

​ 实例变量:所属类中(静态成员除外)

​ 局部变量:所属方法中

(5)内存中生命周期不同:

​ 静态变量:随着类的加载而加载,随着类的回收而消失

​ 实例变量:随着对象的创建而加载,随着对象的回收而消失

​ 局部变量:随着方法的调用而加载,随着方法的出栈而消失

(6)变量的加载方式和次数不同:

​ 静态变量:随着类的加载而加载,只加载唯一的一次

​ 实例变量:随着对象的创建而加载,每创建一次对象就会加载一次

​ 局部变量:随着方法的调用而加载,每调用一次方法就会加载一次

(7)修饰符不同:

​ 静态变量:可以添加修饰符,但必须含有static关键字修饰

​ 实例变量:可以添加修饰符

​ 局部变量:只能被final关键字修饰

2.静态方法

当 static 修饰成员方法时,该方法称为类方法 ,静态方法不属于对象,而是属于类

调用格式:

对于本类当中的静态方法,可以省略类名
//静态变量(类变量)

类名.静态(类)变量名;

//调用静态方法

类名.静态方法名(参数);


也可以使用对象.静态方法名();
如果没有静态方法,必须要先创建对象,然后通过对象才能使用。
    

3.静态方法调用的注意事项:

​ 1.静态方法可以直接访问类变量和静态方法。

​ 2.静态方法不能直接访问普通成员变量或成员方法(静态不能直接调用非静态—>因为内存当中先有静态内容,再有非静态内容)。反之,成员方法可以直接访问类变量或静态方法。

​ 3.静态方法中,不能使用this关键字。

​ 4.静态方法随着类的加载而加载,而且只加载唯一的一次

​ 静态方法加载:静态方法进入静态区

​ 静态方法调用:静态方法进入栈内存

​ 5.静态方法可以通过类名和对象名进行调用,推荐使用类名进行调用

5.静态原理图解

static 修饰的内容:

​ 是随着类的加载而加载的,且只加载一次。

​ 存储于一块固定的内存区域(静态区),所以,可以直接被类名调用。

​ 它优先于对象存在,所以,可以被所有对象共享。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s4AvN21o-1658415050651)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\1654571740442.png)]

6.静态代码块

**特点:**当第一次用到本类时,静态代码块执行唯一的一次(静态内容总是优先于非静态,静态代码比构造方法先执行)

格式:

	public class 类名{
			static{
			//内容
}
}

用途:

​ 用于一次性的对静态成员变量进行赋值

位置:

​ 类中方法外

含义:

​ 被static修饰的代码块

目的:

​ 1.给静态常量进行赋值初始化(暂不涉及)

​ 2.类初始化过程的笔试题(暂不涉及)

​ 3.封装工具类提高某个对象加载的优先级(暂不涉及)

注意:

​ 1.静态代码块随着类的加载而加载,而且只加载唯一的一次

​ 2.在静态代码块中不可以使用非静态成员

​ 3.在静态方法中不可以使用this关键字

2、单例设计模式

设计模式和框架

  •  设计模式:解决某一类问题专用的解决方案
    
  •  框架:半成品项目
    

含义:解决创建唯一实例对象的专用解决方案

分类:

立即加载(饿汉式)

​ 在类加载的同时进行对象的创建

延迟加载(懒汉式)

​ 什么时候使用对象什么时候再进行对象的创建

具体实现

需要:

1

2)在该类内部产生一个唯一的实例化对象,并且将其封装为private static类型。

3)定义一个静态方法返回这个唯一对象

1.立即加载(饿汉式)

立即加载模式的步骤(单线程环境中):

​ 1.将构造器进行私有化

​ 2.在成员位置上创建唯一对象

​ 3.为了在外界可以进行唯一对象的访问,通过static进行修饰

​ 4.为了唯一对象的数据安全性,通过private进行修饰

​ 5.对外提供公共的获取方式

立即加载模式的弊端:

​ 在实际应用中可能加载到唯一对象所对应的类,加载后对象直接在堆内存中进行创建,但是没有马上进行使用,导致内存使用偏低

public class CEO {
    private static CEO ceo = new CEO();

    private CEO () {}

    public static CEO getCEO() {
        return ceo;
    }
}
2.延迟加载(懒汉式)

延迟加载模式(懒汉式,仅适用于单线程环境中)

1.将构造器进行私有化

2.在成员位置上声明唯一对象变量;

3.为了在外界可以进行唯一对象的访问,通过static进行修饰

4.为了唯一对象的数据安全性,通过private进行修饰

5.对外提供公共的获取方式并且在第一次获取对象时进行对象的创建

“懒汉模式”的优缺点:

只有在方法第一次被调用时才会初始化,节省内存空间。

缺点:在多线程环境中,这种实现方法是完全错误的,根本不能保证单例的状态。

public class CEO {
    private static CEO ceo;

    private CEO () {}

    public static CEO getCEO () {
        if (ceo == null) {
            ceo = new CEO();
        }

        return ceo;
    }
}

3、跨包使用类(import关键字)和API文档

1.import关键字

​ 前提:被使用的类或成员的权限修饰符是>缺省的,即可见的

使用import 语句之后,代码中使用简名称(推荐)

import语句告诉编译器到哪里去寻找类。

import语句的语法格式:

import.类名;
import.*;
import static.类名.静态成员;

注意:

使用java.lang包下的类,不需要import语句,就直接可以使用简名称 。

import语句必须在package下面,class的上面。

当使用两个不同包的同名类时,例如:java.util.Date和java.sql.Date。一个使用全名称,一个使

用简名称 。

引用类型一般使用步骤:

​ 1.导包(import 包路径 类名;)

如果使用的目标类和当前类位于同一个包下,则可以省略导包语句(只有java.lang包下的内容不需要导包)

​ 2.创建

类名 对象名=new 类名();

​ 3.使用

对象名.成员方法名();

2.API

应用程序编程接口。

4、API中的类

1、Scanner类

类的特点

​ 针对基本类型数据和字符串类型数组进行简单扫描的工具类

类的位置

​ java.util

类的构造器

​ public Scanner(InputStream source)

​ 构造一个新的 Scanner,它生成的值是从指定的输入流扫描的。

​ 实参:System.in

类的方法

​ public void close()

​ 关闭此扫描器。

​ public byte nextXxx()

​ 将输入信息的下一个标记扫描为一个 xxx基本类型的数据。

​ public String next()

​ 查找并返回来自此扫描器的下一个完整标记

​ public String nextLine()

​ 此扫描器执行当前行,并返回跳过的输入信息。

next():对于"空白符号“,”回车和换行“不会进行扫描

nextLine():会扫描回车和换行,空白字符

Scanner类的注意事项:

1.Scanner类没有提供关于扫描char类型数据的方法

2.Scanner类扫描键盘录入的数据时,录入的数据必须在其扫描的数据类型取值范围内,否则运行报错(InputMismatchException)

3.Scanner类的next()和扫描基本类型数据的方法遇到"空白符号"停止扫描

4.Scanner类的next()和扫描基本类型数据的方法遇到"回车换行"不会扫描

5.使用Scanner类的nextLine()时,需要保证整个方法调用之前,不能使用next()和扫描基本类型数据的方法

2、Math类

java.lang.Math 类包含用于执行基本数学运算的方法,如初等指数、对数、平方根和三角函数。类似

这样的工具类,其所有方法均为静态方法,并且不会创建对象

类的特点

​ 针对数学运算操作的工具类

类的位置

​ java.lang

类的构造器

​ 构造器私有化–>(构造器私有化就不能够创建对象)

类的方法

​ public static double random()

​ 返回带正号的 double 值,该值大于等于 0.0 且小于 1.0。

获取指定范围内数字的小技巧

​ (int)(Math.random() * a + b)

​ a:指定范围内所有数据的个数

​ b:指定范围内起始数字

3、Arrays类

1.类的特点

​ 针对数字进行操作的工具类

2.类的位置

​ java.util

3.类的构造器

​ 构造器私有化—>提供静态方法进行使用

4.类的方法

​ 1)public static String toString(int[] a)

​ 返回指定数组内容的字符串表示形式。

​ 2)public static int binarySearch(int[] a,int key)

​ 使用二分搜索法来搜索指定的 int 型数组,以获得指定的值。

​ 3)public static int[] copyOf(int[] original,int newLength)

​ 复制指定的数组,截取或用 0 填充(如有必要),以使副本具有指定的长度。

​ 4)public static void sort(int[] a)

​ 对指定的 int 型数组按数字升序进行排序。

​ 5)public static void sort(T[] a,Comparator c)

​ 根据指定比较器产生的顺序对指定对象数组进行排序。

5.Arrays.sort()的排序规则:

​ 1)如果是基本类型数组(boolean数组除外):

​ 针对元素数据大小进行升序排序

​ 2)如果是字符串类型数组:

​ 针对元素字符对应的Unicode码表进行升序排序

​ 3)如果是自定义类型数组:

​ 必须手动进行比较器的定义

自然顺序比较器:实现Comparable

定制顺序比较器:实现Comparator

4、System类

类的特点

​ 针对常用属性和方法进行封装的工具类

类的位置

​ java.lang

类的构造器

​ 构造器私有化

类的方法

​ public static long currentTimeMillis()

​ 返回以毫秒为单位的当前时间。

​ public static long nanoTime()

​ 返回最准确的可用系统计时器的当前值,以毫微秒为单位。

5、BigInteger类

类的特点

​ 不可变的任意精度的整数。如果在实际开发中使用到超出long的取值范围时,可以使用BigInteger

类的位置

​ java.math

类的构造器

​ public BigInteger(String val)

​ 将 BigInteger 的十进制字符串表示形式转换为 BigInteger。

6、BigDecimal类

类的特点

​ 不可变的、任意精度的有符号十进制数(精度高—2.0-1.1)。

类的位置

​ java.math

类的构造器

​ public BigDecimal(String val)

​ 将 BigDecimal 的字符串表示形式转换为 BigDecimal。

day09(6.8)

1、继承

类中的共性抽取

1.继承的含义

​ 就是子类继承父类的属性和行为,使得子类对象具有与父类相同的属性、相同的行为

好处:

​ 1.提高代码的复用性,从而提高开发效率

​ 2.提高程序的扩展性

​ 3.学习继承关系是学习"实现关系"的前提条件

​ 4.学习继承关系是学习"多态"的前提条件之一

​ 5.学习继承关系是学习"匿名内部类"的前提条件之一

2.继承的格式
 public class 父类类名 {}

 public class 子类类名 extends 父类类名{}
3.继承的特点

​ 1、单继承

​ 2、可以多级继承

​ 3、一个子类只能有一个直接父类,但是一个父类可以有多个子类

子类可以继承父类的静态成员、静态方法、实例变量、实例方法

对于私有的成员变量:

子类可以继承父类的私有成员,但受限于private关键字的特点无法直接使用,如需访问可以通过公共访问方式进行获取和存储

构造方法的名字是与类名一致的。所以子类是无法继承父类构造方法的。

成员变量重名:

​ 子父类中出现了同名的成员变量时,在子类中需要访问父类中非私有成员变量时,需要使用 super 关键

字,修饰父类成员变量 。

成员方法重名:

​ 使用重写

继承图解(进度)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yhMNteOW-1658415050652)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\1654671487416.png)]

Cat cat=new Cat();
        cat.age=1;
        cat.name="小会";
        cat.eat();
        cat.sleep();
        cat.catchMouse();

首先在运行后会先将带main()方法的类.class加载进入方法区,在main()方法中识别需要加载的字节码文件,先加载父类的字节码文件,附带父类的属性和方法,在加载子类的字节码文件,子类和父类有指向关系(extends)。由于main方法中有cat对象的声明和初始化,会在堆内存中开辟出一块区域(地址值0x666)【因为只有子类对象new的,并不会在堆内存中开辟出父类的相应内存空间】,这块区域包含本类成员引用区和父类成员引用区,会参照方法区.class文件有对应的方法和属性,并为其赋初始值。有对成员变量的赋值,(会先在本类成员引用区找相对应的变量,找不到再去父类成员引用区找)会在堆中进行覆盖赋值,并将对应地址值指向Cat cat,eat()方法的进栈调用,会先在本类成员引用区进行查找,找不到在去父类成员引用区查找调用,然后方法执行完出栈。把字节码文件进入到方法区就叫类加载

2、super关键字和this的几种用法

this关键字的第一种用法:

​ 场景:

​ 子类的实例方法中或子类的构造器中

​ 格式:

​ this.实例变量名;

​ this.实例方法名(实参);

​ 作用:

​ 区分同一个类中同名的实例变量和局部变量

​ 含义:

​ 哪个对象调用了this关键字所在的实例方法或构造器,this关键字就代表哪个对象

super关键字的第一种用法:

​ 场景:

​ 子类的实例方法中或子类的构造器中

​ 格式:

​ super.实例变量名;

​ super.实例方法名(实参);

​ 作用:

​ 区分子父类继承关系中同名实例变量和同名并同参的实例方法

this关键字的第二种用法

场景:

子类的构造器

格式:

this(实参);

作用:

调用本类中其它的构造器

含义:

当构造器本身无法进行初始化时,通过this(实参)调用本类中其它的构造器完成对象成员初始化

super关键字的第二种用法

场景:

子类的构造器

格式:

super(实参);

作用:

根据实参调用父类中的对应的构造器

含义:

在初始化子类对象成员之前,通过super(实参)调用父类中对应的构造器完成父类成员的初始化

this关键字的第三种用法:

场景:

父类的实例方法中或父类的构造器中

格式:

this.实例变量名;

this.实例方法名(实参);

作用:

使用多态形式的对象调用实例变量或实例方法

含义:

哪个对象调用了this关键字所在的构造器或实例方法,this关键字就代表所在类型(this关键字所在的类名)的哪个多态形式对象

super关键字的第三种用法:

场景:

实现类的实例方法中

格式:

父接口名.super.默认方法名(实参);

作用:

调用指定父接口的默认方法

含义:

哪个对象调用的super的实例方法,super就代表哪个对象的指定父接口引用

this关键字的第四种用法:

​ 场景:内部类中使用(成员内部类的实例方法)或成员内部类的构造方法中

​ 格式:外部类类名.this.实例变量名;

​ 外部类类名.this.实例方法名(实参);

​ 作用:用来区分外部类实例方法和内部类实例方法或局部方法同名的情况

​ 用来区分外部类实例变量和内部类实例变量或局部变量同名的情况

那个对象调用了含有外部类名.this的实例方法或构造器,那外部类名.this就是哪个内部类对象的外部类对象

super第四种用法

​ 场景:内部类中使用(成员内部类的实例方法)或成员内部类的构造方法中

​ 格式:外部类类名.super·.实例变量名;

​ 外部类类名.super.实例方法名(实参);

​ 作用:用来区分外部类父类实例方法和内部类父类实例方法或局部方法同名的情况

​ 用来区分外部类父类实例变量和内部类父类实例变量或局部变量同名的情况

那个对象调用了含有外部类名.super的实例方法或构造器,那外部类名.super就是哪个内部类对象的外部类对象的父类引用。

this关键字和super关键字的注意事项

1.在代码中,this关键字可以进行打印,super关键字不可以进行打印

2.this(实参)和super(实参)必须在构造器中的第一行,否则编译报错

3.this(实参)和super(实参)不可以在同一个构造器使用

4.在静态方法和静态代码块中不可以使用this关键字或super关键字

3、覆盖重写

​ 含义:

​ 在子父类继承关系中(或实现关系中),出现了方法名相同,形参列表相同,权限访问级别和返回类型遵循相关规则 的现象

​ 场景:

​ 1.上级布置功能需求时,会提供没有方法实体的方法(将两个明确准备好),需要自己完成过程代码的编写

​ 2.二次开发

方法重写的前提条件:

1.必须有子父类继承关系或实现关系

2.子类重写方法的方法名必须和父类方法的方法名相同

3.子类重写方法的形参列表必须和父类方法的形参列表相同

4.子类重写方法的权限访问级别必须大于或等于比父类方法的权限访问级别,且遵循权限访问级别的修饰范围(详见方法重写的注意事项)

5.子类重写方法的返回类型在内存中必须和父类方法的返回类型相同

在父子类的继承关系当中,创建子类对象,访问成员方法的规则:

​ 创建的对象是谁,就优先用谁,如果没有则向上找。

​ 无论是成员方法还是成员变量,如果没有都是向上找父类,绝对不会向下找子类的。

重载和重写:

​ 重写(Override):方法的名称一样,参数列表【也一样】。覆盖、覆写。

​ 重载(Overload):方法的名称一样,参数列表【不一样】。

方法的覆盖重写特点:创建的是子类对象,则优先用子类方法。

1.注意事项

1.重写必须保证父子类之间方法的名称相同,参数列表也相同。

@Override:写在方法前面,用来检测是不是有效的正确覆盖重写。

这个注解就算不写,只要满足要求,也是正确的方法覆盖重写。

  1. 当父类返回类型为void和基本数据类型时,子类重写时必须和父类一致;父类为返回类型为引用类型时,子类重写时必须要小于等于父类方法返回值类型(java.lang.Object类是所有类的公共最高父类(祖宗类),java.lang.String就是 Object的子类。)

  2. 子类方法的权限必须【大于等于】父类方法的权限修饰符。

小扩展提示:public > protected > 缺省 > private

备注:缺省不是汉字缺省,而是什么都不写,留空。

​ 4.父类的私有方法不能被子类重写

​ 5.父类的静态方法不能被子类进行重写

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vfselpYP-1658415050653)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\1654823438983.png)]

​ 6.父类和子类不在同一个包下,父类的缺省方法不可以被子类进行重写

继承关系中构造器的特点:

1.子类无法继承父类的构造器

2.初始化子类成员之前,先初始化父类成员

3.当构造器中没有任何的this(实参)或super(实参),JVM的编译器自动给其提供一个无参的super(),供其初始化子类成员之前,先初始化父类成员;一旦构造器有this(实参)或super(实参),JVM的编译器不再进行提供

4、object类

public String toString() :返回该对象的字符串表示。 

public boolean equals(Object obj) :指示其他某个对象是否与此对象“相等”。
1.toString()方法

toString()方法返回的是对象的数据类型+@+地址值,如果想要返回特定的字符串则需要对其覆盖重写。

toString()方法的注意事项

  •  使用输出语句打印对象名的时候,其实就是打印该对象的toString()的返回内容(只是没有显示),char类型数组除外
    
char[] chars={'a','b','c'};
        System.out.println(chars);//abc
        System.out.println(chars.toString());//地址值

对于char类型的数组,在使用输出语句时,所输出的chars打印出来是String类型的字符串拼接,因为char[]有对应的默认打印方法,可以接收char类型数组。而在调用toString方法时,由于没有重写Object类的toString(),所以打印出来的是默认的object类的toString方法,打印的是地址值。

2.equals()方法

​ 1)判断两个对象是否相同

​ 2)判断对象的地址值是否相同(在没有覆盖重写的情况下)

eqauls()和运算符==的区别

​ ==既可以比较基本类型数据,也可以比较引用类型数据

​ 当==比较基本类型数据时,比较的是基本类型数据值是否相等

​ 当==比较引用类型数据时,比较的是引用类型地址值是否相等

​ eqauls()只可以比较引用类型数据

​ eqauls()比较的是引用类型地址值是否相等

​ 如果比较类型重写了Object类的eqauls(),需要按照重写后的规则进行比较

day10(6.10)

1、权限访问级别

三种权限修饰符,四种访问权限

​ private(当前类访问权限)

​ (default:缺省)(包访问权限)

​ protected(子类访问权限)

	*如果是三级结构(项目,包,类文件),在同一个项目中并且跨包情况下具有子父类继承关系有效,跨包且没有继承关系时无法进行访问
 	*如果是四级结构(项目,模块,包,类文件),在同一个模块中,具有子父类继承关系有效,跨包且没有继承关系时无法进行访问,如果在同一项目不在同一个模块下,需要手动修改项目的配置文件后,具有子父类继承关系被protected修饰的内容有效

​ public(公共访问权限)

	*如果是三级结构(项目,包,类文件),在同一个项目中有效,跨项目无法进行访问
    *如果是四级结构(项目,模块,包,类文件),在同一个模块中有效,在同一个项目中不同模块,需要手动修改项目的配置文件后有效,跨项目无法进行访问

思考:如果存在多个项目,某些数据需要跨项目进行访问,怎么办?

  •  序列化和反序列化(第一种序列化和反序列化:对象流)
    
private (default) protected public
同一个类(自己) Y Y Y Y
同一个包(邻居) N Y Y Y
不同包子类(儿子) N N Y Y
不同包非子类(陌生人) N N N Y

​ 从大到小: public >protected>(default)>private

权限访问级别的补充:

类文件(class,interface,enum)

​ 权限访问级别:

​ 只能public和缺省,推荐public

class中的成员量(成员变量和成员常量)

​ 权限访问级别:

​ 四种都可以,推荐private

class中的成员方法

​ 权限访问级别:

​ 四种都可以,推荐public,private,protected

class中的成员内部类

​ 权限访问级别:

​ 四种都可以,推荐缺省,private

class中的构造器

​ 权限访问级别:

​ 四种都可以,public,private,protected

class中的构造器代码块

​ 权限访问级别:

​ 只能缺省

class中的静态代码块

​ 权限访问级别:

​ 只能缺省


interface中静态常量

​ 权限访问级别:

​ 只能public,即使没有不写public,JVM的编译器自动填充public

interface中抽象方法

​ 权限访问级别:

​ 只能public,即使没有不写public,JVM的编译器自动填充public

interface中内部接口

​ 权限访问级别:

​ 只能public,即使没有不写public,JVM的编译器自动填充public

interface中默认方法

​ 权限访问级别:

​ 只能public,即使没有不写public,JVM的编译器自动填充public

interface中静态方法

​ 权限访问级别:

​ 只能public,即使没有不写public,JVM的编译器自动填充public

interface中私有方法

​ 权限访问级别:

​ 只能private


enum中的枚举对象

​ 权限访问级别:

​ 只能public,如果显式修饰为public,编译报错

enum的成员量(成员变量和成员常量,除枚举对象)

​ 权限访问级别:

​ 四种都可以,推荐private

enum中的成员方法

​ 权限访问级别:

​ 四种都可以,推荐public,private,protected

enum中的成员内部类

​ 权限访问级别:

​ 四种都可以,推荐缺省,private

enum中的构造器

​ 权限访问级别:

​ 只能private,即使没有不写private,JVM的编译器自动填充private

enum中的构造器代码块

​ 权限访问级别:

​ 只能缺省

enum中的静态代码块

​ 权限访问级别:

​ 只能缺省

代码块中的局部成员:

​ 权限访问级别:

​ 只能缺省


2、抽象

继承关系中的BUG:

1.父类存储多个子类相同的属性和相同的行为,希望这个父类不可以被实例化对象,实际上这个父类可以被实例化对象;

2.父类的实例方法需要子类们进行重写,但是最终执行肯定是子类的重写后方法,父类被重写方法的方法体就不是很重要了,希望在开发中

不编写父类被重写方法的方法体,实际上如果父类被重写方法的方法体省略不写的话编译报错;

3.父类的实例方法需要子类们进行重写,希望在忘记重写时,开发环境编译报错进行提示,实际上什么提示都没有

1、父类在被子类继承之后,还是可以被实例化对象

2、在子类重写了父类的方法后,父类的方法不会再执行,编写父类方法中的方法体没必要。但去掉方法体会报错

3、父类中的方法需要被重写,但不重写系统也不会提示和报错。

抽象的出现就是为了解决继承关系中上述的3个BUG

抽象类中不一定有抽象方法,但抽象方法的类必须为抽象类

1.抽象方法

没有方法体的方法

如果父类当中的方法不确实如何进行{}方法体的实现,那他就是一个抽象方法。(给出不具体的东西,比如动物吃什么)

格式:

修饰符 abstract 返回值类型 方法名 (参数列表)
2.抽象类

(抽象方法所在的类必须为抽象类)

被abstract关键字修饰的类

格式:

(修饰符) abstract class 类名字 { 
    
}
3.抽象方法和抽象类的使用:

​ 1)抽象类不能通过new进行创建

​ 2)必须要通过子类来继承抽象父类

​ 3)子类必须覆盖重写抽象父类中所有的抽象方法

覆盖重写格式

public abstract class Animal {
    public abstract void sleep();
}
public class Cat extends Animal{
    //对Animal抽象父类的抽象方法sleep()进行覆盖重写
    @Override
    public void sleep() {
        System.out.println("睡觉");
    }
}

​ 4)创建子类对象,调用方法

4、抽象方法和抽象类的用处
5、注意事项

1)抽象类不能创建对象

2)抽象类中,可以有构造方法,作用是用来创建子类对象时先初始化父类成员的【在继承关系当中,初始化子类成员之前要先通过super()初始化父类的成员。】

//父类
public class supClass {    
    public supClass() {        
        System.out.println("父类构造方法");    
    }
}
//子类
public class subClass extends supClass{   
    public subClass() {
         //super();//有一个默认的隐藏的super();用来创建子类对象时先初始化父类成员
        System.out.println("子类构造方法");   
    }
}

3)抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类

4) 抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象类

3、final关键字

abstract关键字和final关键字不能同时使用,两者矛盾。

abstract:一定被覆盖重写

final:不能被覆盖重写

​ 被final修饰的类就是最终类(不可改变类),不可以被继承

​ 被final修饰的方法就是最终方法(不可改变方法),不可以被重写

​ 被final修饰的变量就是最终量(不可改变量,其实就是自定义常量),不可以被重新赋值

1.修饰一个类

​ 格式:

final class 类名 {

}

这个类就不能有任何子类(太监类)

一个类被final修饰,则它所有的成员方法都不能够覆盖重写(不能被继承)

2.修饰一个方法

​ 格式:

修饰符 final 返回值类型 方法名(参数列表){
    //方法体
}

这个方法不能被覆盖重写

3.修饰一个局部变量

一旦使用final进行修饰,则这个变量就不能够改变

​ 1)基本类型

​ 被final修饰后,只能赋值一次,不能再更改

​ 2)引用类型

​ 被final修饰后,只能指向一个对象,地址不能再更改。但是不影响对象内部的成员变量值的修改

4.修饰一个成员变量(实例变量和静态变量)

​ 1、由于成员变量都有默认值,所以用来final之后,必须进行手动赋值,不会再给默认值

​ 2、对于final的成员变量,要么直接赋值,要么通过构造方法赋值,二者选其一

​ 3、必须保证类当中的所有重载的构造方法,都会最终对final的成员变量进行赋值显示初始化

public class User { 

final String USERNAME = "张三"; 

private int age; 

}

构造方法初始化。

public class User { 
    final String USERNAME ; 
    private int age; 
    public User(String username, int age) { 
        this.USERNAME = username;
        this.age = age;
    }
}
//被final修饰的常量名称,一般都有书写规范,所有字母都大写。

实例常量

含义:

被final修饰的实例变量

注意:

1.堆内存在识别实例变量被final修饰后,不会给其进行默认值赋值操作

2.实例常量如果进行先声明后初始化操作,需要在实例常量所在类的所有构造器中给其进行赋值操作

如果所有的构造器中初始化赋值的数据相同,直接在构造器代码块中进行简化操作

如果所有的构造器中初始化赋值的数据不同,只能在各自构造器中进行赋值操作

4、接口

1.概念
2.定义格式

接口的定义,它与定义类方式相似,但是使用 interface 关键字。它也会被编译成.class文件,但一定要明确它并不是类,而是另外一种引用数据类型。

 //声明格式

【修饰符】 interface 接口名{ 
//接口的成员列表: 
    // 公共的静态常量
    // 公共的抽象方法
    // 公共的默认方法(JDK1.8以上)
    // 公共的静态方法(JDK1.8以上) 
    // 私有方法(JDK1.9以上) 
    // 内部接口 
}
3、接口成员说明
1.公共的静态常量
(public static final) 数据类型 变量名 = 数据值;public】 【static】 【final】这三个关键字均可省略
        
    (不能先声明再初始化)

变量必须进行直接赋值,一旦赋值不可改变;变量名完全大写,用下划线进行分隔。接口中的静态常量可以被实现类获取

2.公共的抽象方法
(public abstract) 返回值类型 方法名(参数列表);

实现类必须覆盖重写接口所有的抽象方法,除非实现类是抽象类

3.默认方法((JDK1.8以上)
(public) defalut 返回值类型 方法名(参数列表){
    方法体
}
默认方法其实是类中的实例方法,但是接口不能实例化对象。就将实例方法改成默认方法。
    
    1、源码中
    2、面试题中

接口中的默认方法可以通过接口实现类对象,直接调用

默认方法也可以被覆盖重写

默认方法的注意事项:

1.当子类继承父类,也实现父接口,父类中实例方法和父接口中的默认方法同名,当子类调用该方法时,

​ 如果子类重写该方法时,执行子类重写后的该方法;

​ 如果子类没有重写该方法,执行父类的实例方法;

2.当子类不继承父类,但实现多个父接口,多个父接口中存在同名的默认方法,子类必须以实例方法重写该同名的默认方法

4.公共的静态方法(JDK1.8以上)
(public) static 返回值类型 方法名(参数列表){
    方法体
}


静态方法在接口实现关系中,不能够被继承
    静态常量可以被继承

通过接口名称进行调用,不能通过实现类对象进行调用接口的静态方法

调用方式:

​ 接口名.静态方法(参数)

5.私有方法(JDK1.9以上)

1)普通私有:(用来解决默认方法之间有重复代码)

private 返回值类型 方法名(参数列表){

	方法体

}

2)静态私有

private static 返回值类型 方法名(参数列表){

	方法体

}

private 的方法只能通过接口才能进行调用,不能被其他类调用

除此之外,接口中不能有其他成员,没有构造器,没有初始化块,因为接口中没有成员变量需要动态初始化。

1、为什么接口中只能声明公共的静态的常量?

因为接口是标准规范,那么在规范中需要声明一些底线边界值,当实现者在实现这些规范时,不能去随意修改和触碰这些底线,否则就有“危险”。

**2、为什么JDK1.8之后要允许接口定义静态方法和默认方法呢?**因为它违反了接口作为一个抽象标准定义的概念。

**3、为什么JDK1.9要允许接口定义私有方法呢?**因为我们说接口是规范,规范时需要公开让大家遵守的

私有方法:因为有了默认方法和静态方法这样具有具体实现的方法,那么就可能出现多个方法由共同的代码可以抽取,而这些共同的代码抽取出来的方法又只希望在接口内部使用,所以就增加了私有方法。

4、接口的实现

接口不能够创建对象,但是能够被类实现(implements关键字)

【修饰符】 class 实现类 implements 接口{ 

// 重写接口中抽象方法【必须】,当然如果实现类是抽象类,那么可以不重写 

// 重写接口中默认方法【可选】 

}

【修饰符】 class 实现类 extends 父类 implements 接口{ 

// 重写接口中抽象方法【必须】,当然如果实现类是抽象类,那么可以不重写 

// 重写接口中默认方法【可选】 

}
使用接口注意事项:

​ 1.如果接口的实现类非抽象类,那必须重写接口中所有的抽象方法

​ 2.接口中的默认方法可以被重写,可以不重写(重写时,default去掉)

​ 3.接口中的静态方法不能被继承也不能被重写,要调用接口中的静态方法,用接口名调用

​ 4.对于非静态方法,只能通过实现类对象进行调用(接口不能直接创建对象,只能创建实现类对象)

​ 5.接口没有静态代码块或构造方法

​ 6.一个类的直接父类唯一,但一个类可以实现多个接口(中间用逗号隔开)

​ 7.如果实现类所实现的多个接口中有重复的抽象方法,只需要覆盖重写一次

​ 8.如果实现类没有重写接口的所有抽象方法,那这个实现类一定是抽象类

​ 9.如果实现类所实现的多个接口中,存在着重复的默认方法,那实现类一定要对冲突的默认方法进行覆盖重写

​ 10.一个类如果直接父类当中的方法和接口当中的默认方法产生冲突,优先使用父类的方法

​ 11.当实现类有直接父类又实现若干个接口时,父类中的成员方法与接口中的抽象方法重名,子类就近选择执行父类的成员方法。

​ 12.子接口重写默认方法时,default关键字可以保留。 子类重写默认方法时,default关键字不可以保留

总结

总结

1、在类中有的东西

【实例归属于对象,随着对象的创建而加载;静态归属于类,随着类的加载而加载。】

1.成员量(成员变量、成员常量)

​ 成员变量(类中方法外,没有final修饰)

​ 静态变量:static修饰的成员变量

​ 实例变量:没有static进行修饰的成员变量

​ 成员常量(类中方法外,有final关键字修饰)

​ 静态常量:有static final修饰

​ 实例常量:没有static有 final修饰

2.成员方法

​ 静态方法:static修饰的方法

​ 调用格式:类名.静态方法名(或对象名.静态方法名)【推荐用类名调用】

​ 注意事项:

​ 1、静态方法不能访问非静态

​ 2、不能使用this关键字

​ 3、随着方法的加载而加载,只加载唯一的一次

​ 实例方法

​ 调用实例方法的三种方式:

​ 1、直接调用:对象名.方法名(实参);

​ 2、输出调用:System.out.println(对象名.实例方法名(实参));

​ 3、赋值调用:数据类型 变量名 = 对象名.实例方法名(实参);

​ System.out.println(变量名);

3.成员内部类

​ 某个类只想本类或本包进行访问,不想被外界进行访问,可以将这个类以内部类形式声明到某个类的成员中。

静态成员内部类

​ 格式:[内部类修饰符四种都可以,推荐private和缺省]

		【修饰符】 class 外部类{ 
            
  		  【修饰符】 static class 内部类{ 
  	      
  	 		 } 
		}

​ 注意事项:

​ 1、如果想访问静态内部类中的静态成员

​ 1)在该静态内部类的外部类的外界进行访问

​ 外部类类名.内部类类名.静态变量名;

​ 外部类类名.内部类类名.静态变量方法(实参);

​ 2)在该静态内部类的外部类的内部访问

​ 内部类类名.静态变量名;

​ 内部类类名.静态变量方法(实参);

​ 2、静态内部类中有静态成员也有实例成员,如果在外界访问内部类中的实例成员,要创建 内部类对象

​ 3、在外部类的成员中可以访问内部类的私有静态成员

实例成员内部类

​ 格式:[四种都可以,推荐private和缺省]

		【修饰符】 class 外部类{ 
            
    		【修饰符】  class 内部类{ 
                
        
   			 } 
		}

内用外,随意访问;外用内,需要内部类对象。

​ 直接方法:

​ 外部类名.内部类名 对象名 = new 外部类名().new 内部类名();

​ 如果在内部类中访问同名的外部类实例变量:

		外部类名.this.外部类成员变量名

2、在方法中的东西

方法的概念(不调用,不执行)

修饰符  返回值类型  方法名  (参数1数据类型   参数1名,.....{

			方法体语句

			return  返回值;

}
返回值类型:基本数据类型(四类八种)和引用数据类型(数组、类、接口),以及void无返回值类型

方法执行的两个关键因素:传参和返回
    传参:将实参传递给形式参数的过程,在自定义方法要调用方法中的数据时,需要传参进行数据传递
    		实参:调用方法时()当中的参数,它是具体的数值或地址值
    		形参:声明方法时()当中的参数,它是声明变量类型和变量名
    返回:调用自定义方法时,将自定义方法结果返回给调用方法的过程
   
    
    方法的调用:(三种调用方式)
    1、直接调用
    类名.方法名();
	如果没有返回值类名(void)的方法,只能直接调用
    2、输出调用
	通过输出语句进行方法的调用
    3、赋值调用
    数据类型  变量名 = 方法名(参数);

重载

在同一个类或(继承关系)中,出现了方法名相同但形参列表不同的情况。

参数列表不同指的是:

​ 参数的数据类型不同

​ 参数的数量不同

​ 参数的数据类型的顺序不同

它是与参数名无关,与方法的返回值类型无关

1.局部变量(方法中或方法声明上)【没有默认值,在栈内存,属于方法,只能被final关键字修饰】

局部变量在声明时必须被初始化。

​ 如果方法的参数是局部变量,参数在方法的调用时,必须被赋值

​ 局部变量是随着方法的调用而加载,调用一次就加载一次

2.局部常量:被final修饰,这个变量就不可改变

​ 1、基本数据类型

​ 被final修饰后,只能赋值一次,不可再更改

​ 2、引用类型

​ 被final修饰,只能指向一个对象,地址值不可改变

3.局部内部类

局部内部类

​ 1、局部内部类不可被static进行修饰

​ 2、不能在所属方法外部实例化,在所属方法内进行内部类对象的实例化

​ 3、局部内部类所属方法的外部类局部变量和该局部内部类中的实例变量或局部变量同 名,则该局部内部类中无法访问

​ 4、局部内部类所属方法的外部类局部变量如果重新赋值,则无法在该局部内部类中使 用

​ 5、局部内部类所属方法的外部类局部变量如果在局部内部类中使用,jvm的编译器会 自动将其隐式修饰为final

匿名内部类(只能缺省)

​ 前提:必须继承一个父类或者实现一个接口

​ 注意:匿名内部类是对于实现类的匿名

​ 格式:

	new 父类名或者接口名(){ 
    // 方法重写 
    @Override public void method() {
        // 执行语句 
    } 
};


接口名 对象名 = new 接口名称(){

    //覆盖重写所有的抽象方法

}1、匿名内部类只能使用唯一的一次

2、 匿名对象在调用方法时只调用唯一的一次 

3、接口中有的东西

1.静态常量
	(public static final) 数据类型 变量名 = 数据值;public】 【static】 【final】这三个关键字均可省略
 
  变量必须进行直接赋值,一旦赋值不可改变;变量名完全大写,用下划线进行分隔。接口中的静态常量可以被实现类获取
2.抽象方法
	(public abstract) 返回值类型 方法名(参数列表);
	实现类必须覆盖重写接口所有的抽象方法,除非实现类是抽象类
3.默认方法(JDK1.8)
	(public) defalut 返回值类型 方法名(参数列表){
    	方法体
	}
	默认方法其实是类中的实例方法,但是接口不能实例化对象,就将实例方法改成默认方法。
    接口中的默认方法可以通过接口实现类对象,直接调用。
    默认方法也可以被重写。
    注意事项:
      1、当子类继承父类时,也实现父接口,父类中的实例方法和父接口中的默认方法同名,当子类调用该方法时
        如果子类重写该方法,执行子类重写后的该方法
        如果子类没有重写,执行父类的实例方法
      2、当没有继承父类,实现多个父接口,多个父接口存在同名的默认方法,子类必须重写该同名的默认方法
4.静态方法(JDK1.8)
	(public) static 返回值类型 方法名(参数列表){
    	方法体
	}


	静态方法在接口实现关系中,不能够被继承
    静态常量可以被继承
    
   调用方式:接口名.静态方法(参数)
5.私有方法(JDK1.9)

1)普通私有:(用来解决默认方法之间有重复代码)

private 返回值类型 方法名(参数列表){

	方法体

}

2)静态私有

private static 返回值类型 方法名(参数列表){

	方法体

}

private 的方法只能通过接口才能进行调用,不能被其他类调用

除此之外,接口中不能有其他成员,没有构造器,没有初始化块,因为接口中没有成员变量需要动态初始化。

6.内部接口

4、javaBean标准类中有的

//私有化实例变量 【必须】 
//无参构造器【必须】 
//实例方法 
//getXxx() 【必须】 
//setXxx()【必须】 
//有参构造器【可以有】
//构造器代码块【可以有】
//静态代码块【可以有】
//成员内部类【可以有】
//toString(),equals(),hashCode()【可以有】
构造器

专门用来创建对象的方法,当我们new关键字来创建对象时其实就是调用构造方法。

java中至少包含一个默认提供的隐式的构造方法(一旦编写默认的就不会有)

构造器不是方法,可以被重载,构造器的名必须和所在类名一致,且没有返回值类型。

构造器中秋有隐式代码如super(),在子类初始化之前在子类构造方法中的super();先进行父类的成员的初始化

构造器中的{}有很多隐式的代码,执行的时候遵循优先级(实例初始化过程)

​ 第一优先级:隐式或显式的super(实参),显式的this(实参)

​ 第二优先级:实例成员和构造器代码块【谁在前谁先执行】

​ 第三优先级:构造器中除了第一优先级的显式代码

5、关系

1.类与类的关系

​ 单继承

2.类与接口的关系

​ 多实现(中间用,隔开)

3.接口与接口的关系

​ 多继承


修饰符

(由小到大)

private

​ 一旦使用private关键字,则在本类中可进行访问,超出本类就要通过getter/setter方法间接访问。

​ setter没有返回值,参数类型和成员变量对应

​ getter没有参数,返回值类型和成员变量对应

​ 对于boolean,getter方法写成isXxx()

缺省

在同一个包内可进行访问

protected

如果有继承关系的情况下,可以进行跨包访问

public

在同一个项目中可以进行访问,跨项目不可。

具体权限访问级别见day10


面向对象三大特征:封装、继承、多态

1、封装

​ 给程序中不同内容添加不同级别的访问修饰符

​ 步骤:使用private修饰成员变量;再提供getter、setter方法让外部访问

2、继承(extends)

​ 【类的共性抽取】

​ 类与类的关系是:单继承,一个父类可以被多个子类继承。一个类只能继承一个直接父类,但是可以间接继承多个父类(子类继承父类,父类继承他的父类…)。

子类可以继承父类的实例方法、实例变量、静态成员、静态方法

​ 私有方法

​ 可以被继承,但是不能直接访问,只能通过公公的访问方式

​ 构造器

​ 不能被继承,构造器方法名与父类名一致。

​ 当父子类出现同名的成员变量,在子类中调用父类的非私有的变量,用super

​ 如果出现子类和父类成员方法同名,就要重写

重写

​ 在继承关系和实现关系中,出现子父中的成员方法同名且参数一致的情况。(子类重写方法的权限修饰符必须大于或等于父类被重写方法权限修饰符范围)

​ 【子父类返回类型在内存中和父类返回类型相同】

​ 【父类的静态方法和私有方法不能够被重写】

​ 【父类与子类不在同一个包下,父类缺省的方法不能被子类重写】

​ 【在初始化子类成员之前要先初始化父类成员】(执行子类构造方法,在首行有隐式提供的无参 super关键字,先执行完父类在子类)

3、多态

​ 继承关系或实现关系

​ 必须要有方法的重写

​ 构造器的执行和多态无关

​ 实例变量的执行和多态无关

​ 父类指向子类对象:父类对象 对象名 = new 子类对象(实参);

​ 父接口指向实现类对象:父类接口 对象名 = new 实现类名(实参);

在多态中实例方法:

在对象调用实例方法时,先进行虚调用父类(父接口)中的被重写方法,但不执行,如果父类中没有该方法编译报错,如果有则实调用子类(实现类)重写过的方法。

多态的好处:

​ 在声明方法时,如果声明多个子类类型作为形参的重载方法,而且他们的父类(父接口)相同,直接声明一个方法就可以,让相同的那个父类(父接口)作为形参类别。

如要创建多个喂养动物方法
public void  keepPet(Dog dog,String something) {}
public void  keepPet(Cat cat,String something) {}
他们的父类为同一个
则可以将父类作为形参列表,通过传递子类,也可以实现方法的重写和调用
public void  keepPet(Animal a,String something) {}

但是多态形式的对象,无法调用子类(实现类)的特有方法

引用类型的转型

​ 向上转型(自动转型)

父类名称 对象名=new 子类名称();

​ 将子类转成父类,进行使用,此时对象名父类型的

​ 向下转型(强制转型)

子类类型 变量名 = (子类类型) 父类变量名;

​ 在向上转型后,就成为了父类类型使用,就无法调用子类的特有方法,这是就向下转型将父类 转换成子类进行使用,可调用子类的特有方法。

​ instanceof的使用,就是为了判断在向下转型时是否是要转型的子类类型,保证其安全性。

​ 对象名 instanceof 数据类型


4、 几个关键字

static

【静态的都是在堆内存中,属于类】

修饰:成员变量、成员方法、成员内部类、代码块

被其修饰的推荐通过类名调用

静态属于类,随着类的加载而加载,随着类的回收而消失。且只加载唯一的一次

​ 静态变量

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

​ 类中方法外

​ 也可以使用对象名.变量名

静态常量

​ 被 修饰符 static final进行修饰的成员变量

接口中的静态常量:

	(public static final) 数据类型 变量名 = 数据值;public】 【static】 【final】这三个关键字均可省略
变量必须进行直接赋值,一旦赋值不可改变;变量名完全大写,用下划线进行分隔。接口中的静态常量可以被实现类获取

静态方法

​ 调用格式:类名.静态方法名(参数);

​ 也可以使用对象名.变量名

​ 注意:

​ 继承(实现)关系中的静态方法不能被重写

​ 静态方法不能访问非静态内容

​ 静态方法中不能使用this关键字

	接口中:
    
		(public) static 返回值类型 方法名(参数列表){
        
    			方法体
		}


		静态方法在接口实现关系中,不能够被继承
    	静态常量可以被继承
    
 	  调用方式:接口名.静态方法(参数)

静态代码块:

​ 用于一次性的对静态成员变量进行赋值

​ 类中方法外

​ 静态代码块中不能使用this关键字

​ 不能使用非静态成员

​ 静态代码块和静态成员,谁在前谁先加载

静态成员内部类

final

final关键字和abstract关键字不能同时使用,final是不能被重写,abstract必须被重写

​ 被final修饰的类不能被继承(太监类)

​ 被final修饰的方法(不能被覆盖重写)

​ 被final修饰的局部变量

​ 如果是基本类型(只能赋值一次,不能再更改)

​ 如果是引用类型(只能指向一个对象,地址值不在更改。但是不影响对象内部的成员变量值的修改)

​ 被final修饰的成员变量(实例变量和静态变量)

​ 1、由于成员变量都有默认值,所以在被final之后,必须手动赋值,不再给默认值

​ 2、对于final的成员变量要么直接赋值,要么通过构造方法赋值

​ 3、实例常量(被final修饰的实例变量)可以先声明后初始化,需要在实例常量所在的类的所有构造器中(包括无参,有参,重载后的构造方法)给其进行赋值操作。

如果所有构造器中的初始化赋值的数据相同,直接写在构造代码块中进行操作。

如果所有的构造器中初始化赋值的数据不同,只能在各自构造器中进行赋值操作。

this

第一种用法

​ 在子类实例方法或子类构造方法中

​ this.实例变量名;

​ this.实例方法(实参);

他是用来区分同一个类中同名的实例变量和局部变量

​ 哪个对象调用了this关键字所在的实例变量和构造方法,this就代表哪个对象。

第二种用法

​ 子类的构造方法中

​ this(实参);【必须在第一行】

​ 调用本类当中的其他构造方法

​ 当构造器本身无法进行初始化时,通过this(实参)调用本类当中的其它构造方法完成对象成员的初始化

第三种用法

​ 父类的构造方法和父类的实例方法中

​ this.实例变量名

​ this.实例方法名(实参);

​ 使用多态形式的对象调用实例变量或实例方法

第四种用法

​ 内部类当中(成员内部类的实例方法)或成员内部类的构造方法中

​ 外部类类名.this.实例变量名;

​ 外部类类名.this.实例方法名(实参);

​ 用来区分外部类实例方法(实例变量)和内部类实例方法(实例变量)同名的情况

​ 哪个对象调用了含有外部类类名.this的实例方法或构造方法,外部类类名.this就是内部类对象的外部类对象

super

第一种用法

​ 子类实例方法或子类构造方法中

​ super.实例变量名

​ super.实例方法名

​ 区分子父类继承关系中同名的实例变量和同名并同参实例方法名

第二种用法

​ 子类的构造方法中

​ super(实参);

​ 根据实参调用父类中对应的构造方法

​ 在初始化子类对象成员之前,通过super(实参)调用父类中的构造方法完成父类成员的初始化

第三种用法

​ 实现类的实例方法中

​ 父类接口名.super.默认方法(实参);

​ 调用指定父接口的默认方法

哪个对象调用的super的实例方法,super就代表哪个对象指定的父接口引用

第四种用法

​ 内部类中使用(成员内部类的实例方法)或成员内部类的构造方法中

​ 外部类类名.super.实例变量名;

​ 外部类类名.super.实例方法名(实参);

​ 用来区分外部类父类的实例方法和内部类父类的实例方法或局部变量同名的情况

​ 用来区分外部类父类实例变量和内部类父类实例变量或局部变量同名的情况

this和super的注意事项

1、this可进行打印,super不可进行打印

2、this(实参)和super(实参)必须放在构造方法的第一行,否则编译报错

3、this(实参)和super(实参)不能再同一个构造方法中

4、静态方法和静态代码块中不能使用this和super

abstract

抽象方法

修饰符 abstract 返回值类型 方法名 (参数列表);
    
    特点:没有方法体
   	被继承之后,子类必须重写父类所有的抽象方法,除非子类也是抽象类

抽象类

修饰符) abstract class 类名字 { 
   
}

	抽象类不能够实例化(不能被new),只能通过子类来继承抽象父类,在继承之后必须重写父类所有的抽象方法,除非子类也是抽象类
    抽象类中可以有构造方法,是为了创建子类对象时先初始化父类成员
        
   如果抽象方法所在的是接口,那实现类也必须重写父接口中所有的抽象方法

关系:抽象类不一定有抽象方法,但抽象方法所在的类一定是抽象类

5、 引用数据类型:

类、接口

数组

​ 数组当中的多个数据必须为同一种数据类型(数组中的元素可进行数据类型转换,保证内存中的是同一种数据类型即可)

​ 数组长度一旦进行初始化,长度就不可改变

​ 静态初始化的简化版不可以先声明后初始化

​ 原因:静态数组初始化简化版在声明初始化时,java虚拟机(jvm)会根据数组名前的数据类型[] 在内存中申请并开辟内存空间,new 数据类型[]是由jvm隐式提供的;

​ 而先声明再初始化时,无法直接获取数组名前面的数据类型[],jvm不会隐式提供new 数据类型[]

数组的声明方式:

​ 数据类型[] 数组名;

​ 数据类型 数组名[];

数组的初始化

​ 1、静态初始化(只初始化数组元素,不初始化数组的长度)

​ 数据类型[] 数组名= new 数据类型[]{数值1,数值2…}

​ 数据类型[] 数组名={数值1,数值2…}

​ 2、动态初始化(只初始化数组长度)

​ 数据类型[] 数组名=new 数据类型[数组长度];

​ 数组的索引从0开始,最大索引为数组长度-1

6、枚举类:

​ 针对创建固定数量对象的简化方式

枚举类的格式:

public enum 枚举类类名 {
    
 		对象名1(实参),对象名2(实参),......,对象名n(实参);
    
	   }

1、枚举类中没有构造器时,jvm会自动提供一个无参的私有构造器,一旦创建,不在提供(构造器只能private修饰,不写自动隐式补全)

2、枚举类对象必须在类中第一行,上面有其他代码,报错

3、枚举类中的对象如果是通过无参构造器进行创建,那对象后的()可以省略

4、每个枚举类都隐式单继承了Enum类,无法在继承其他类和被其他类继承。但可以实现多个接口

5、枚举类的对象都是由private static final隐式修饰,显示修饰时会报错

6、枚举类无法new进行实例化对象的创建,否则编译报错

7、枚举类无法使用super(实参);


day11(6.11)

1、多态

1.多态的概述

​ 指的是同一行为,具有多个不同表现形式

2.程序中的多态:

对象在程序编译和运行的过程中有两种数据类型,分别为编译时类型和运行时类型

​ 单态:编译时类型和运行时类型一致

​ 举例:Student s = new Student();

​ 多态:编译时类型和运行时类型类型不一致

​ Animal a = new Cat();

3.多态的前提条件

1.必须要有继承关系或实现关系

2.必须要有方法的重写(没有方法重写也可以从格式的角度构成多态,但是这样的多态没有任何实际意义)

如果是继承关系:

​ 1)父类的引用指向子类对象:

		父类类名 对象名 = new 子类类名(实参);

​ 2)父接口的引用指向实现类对象:

		父接口名 对象名 = new 实现类名(实参);

特殊情况下的多态(笔试题):

​ this关键字的第三种用法(见day12)

构造器的执行和多态没有任何关系

实例变量的执行和多态没有任何关系

4.多态关系下实例方法的特点:

多态关系下对象调用实例方法,先进行虚调用父类或父接口的被重写方法,如果父类或父接口存在该方法,实调用子类或实现类重写后的方法;如果父类或父接口不存在该方法,编译报错;

​ 实调用:

​ 平时调用方法,调用的是静态方法或实例方法

​ 虚调用:

​ 调用方法,不会执行方法实体

​ 判断所调用方法在代码中是否存在

当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,执行的是子类重写后方法。

5.多态的好处和弊端

好处:

  •  声明方法时,如果需要声明多个引用类型作为形参的重载方法,且多个引用类型存在相同的父类或父接口,直接声明一个方法即可,形参类型就是多个引用类型的父类或父接口,从而提高程序的复用性和开发效率???
    

多态的弊端:

  •  **多态形式的对象无法调用子类或实现类的特有方法**
    

模拟静态在实际开发中的使用(了解一下多态的开发使用)

见(day11-com\atguigu\poly\demo06)

  //方式1:接口的基本使用(理解并默写)
        StringUtils.method(new SubClass());
        System.out.println("=====================");

        //方式2:匿名内部类(演示,周一讲解)
        StringUtils.method(new SuperInterface() {
            @Override
            public void print(String str) {
                System.out.println(str);
            }
        });
        System.out.println("=====================");

        //方式3:Lambda表达式(演示,day23讲解)
        StringUtils.method(str -> System.out.println(str));
        System.out.println("=====================");

        //方式4:方法引用(演示,day23讲解)
        StringUtils.method(System.out::println);
6.引用类型转型

​ 前提:

​ 具有子父类继承关系或实现关系的类之间的转换

​ 分类:

​ 向上转型:

​ 将子类类型的对象转换成父类类型(多态)

​ 向下转型:

​ 将父类类型的对象转换成子类类型

​ 注意:

​ 进行向下转型的时候,可能会发生类型转换异常 (ClassCastException)

1)向上转型

多态本身是子类向父类向上转型的过程(默认的),当父类引用指向一个子类对象时,就是向上转型。【一定是安全的】

​ 格式:

		父类名称  对象名=new 子类名称();

在右侧创建了一个子类对象,把他作为父类来看待使用

2)向下转型

在向上转型后,就成为了父类类型使用,就无法调用子类的特有方法,这是就向下转型将父类转换成子类进行使用,可调用子类的特有方法。

父类类型向子类类型向a下转换的过程,这个过程是强制的。

		子类类型 变量名 = (子类类型) 父类变量名;
7.instanceof关键字:??

​ 含义:

​ 包含

​ 作用:

instanceof的使用,就是为了判断在向下转型时是否是要转型的子类类型,保证其安全性。

​ 判断指定的对象在内存中的本质是否为指定类型或指定类型的子类类型,如果是返回true,如果不是返回false

​ 格式:

​ 对象名 instanceof 数据类型

2、内部类(重点:匿名内部类)

1.概述:将一个类A定义在另一个类B里面,里面的那个类A就称为内部类,B则称为外部类

1.内部类的形式
1)成员内部类

需求:某个类只想本类中进行访问或者本包内可以访问,不想被外界所访问,可以将这个类声明到某个类的成员中

(1)静态成员内部类

学习目的

​ 某个类只想本类中进行访问或者本包内可以访问,不想被外界所访问,且该类含有静态成员或想提高该类的加载时机,可以将这个类以静态成员内部类的形式声明到某个类的成员中

权限访问级别

​ 四种都可以,推荐private,缺省

格式

【修饰符】 class 外部类{ 
    【修饰符】 static class 内部类{ 
        
    } 
}

注意事项

1.如果想访问静态成员内部类中的静态成员

​ 如果在该静态成员内部类的外部类的外界进行访问:

	外部类类名.内部类类名.静态变量名;

	外部类类名.内部类类名.静态变量方法(实参);

​ 如果在该静态成员内部类的外部类的内部进行访问:

	内部类类名.静态变量名;

	内部类类名.静态变量方法(实参);

2.静态成员内部类中既可以有静态成员,也可以有实例成员,但是如果在外界访问内部类中的实例成员,还需要创建该内部类的对象

3.在外部类的成员中可以访问内部类的私有静态成员

(2)非静态成员内部类(实例内部类)

​ 学习目的:

​ 某个类只想本类中进行访问或者本包内可以访问,不想被外界所访问,且该类的所有成员都是非静态的,可以将 这个类以实例成员内部类的形式声明到某个类的成员中

​ 权限访问级别

​ 四种都可以,推荐private,缺省

​ 成员内部类的定义格式:

【修饰符】 class 外部类{ 
    【修饰符】  class 内部类{ 
        
    } 
}

内用外,随意访问;外用内,需要内部类对象。

使用成员内部类:

​ 1、间接方法

​ 在外部类的方法中,使用内部类,通过main方法调用外部类的方法。

​ 2、直接方法

	外部类名.内部类名  对象名  =  new  外部类名().new  内部类名();

如果在内部类中访问同名的外部类实例变量,格式为:

	外部类名.this.外部类成员变量名

this关键字的第四种用法:

​ 场景:内部类中使用(成员内部类的实例方法)或成员内部类的构造方法中

​ 格式:外部类类名.this.实例变量名;

​ 外部类类名.this.实例方法名(实参);

​ 作用:用来区分外部类实例方法和内部类实例方法或局部方法同名的情况

​ 用来区分外部类实例变量和内部类实例变量或局部变量同名的情况

那个对象调用了含有外部类名.this的实例方法或构造器,那外部类名.this就是哪个内部类对象的外部类对象

super第四种用法

​ 场景:内部类中使用(成员内部类的实例方法)或成员内部类的构造方法中

​ 格式:外部类类名.super.实例变量名;

​ 外部类类名.super.实例方法名(实参);

​ 作用:用来区分外部类父类实例方法和内部类父类实例方法或局部方法同名的情况

​ 用来区分外部类父类实例变量和内部类父类实例变量或局部变量同名的情况

那个对象调用了含有外部类名.super的实例方法或构造器,那外部类名.super就是哪个内部类对象的外部类对象的父类引用。

public class SupClass {
    int num=10;
}

----------------------------------------------------------------
public class SubClass extends SupClass{
    int num=20;
    class inner{
        int num=30;
        public void method(){
            int num=40;
            System.out.println(num);//40		就近原则
            System.out.println(this.num);//30	使用this.变量名调用当前类变量
            System.out.println(SubClass.this.num);//20	使用外部类名.this.num调用外部类变量
            System.out.println(SubClass.super.num);//10	使用外部类名.super.num调用父类变量
        }
    }

}
-------------------------------------------------------------------
public class TestMain {
    public static void main(String[] args) {
        SubClass.inner inner = new SubClass().new inner();
        inner.method();
    }
}

实例内部类注意事项:

​ 1.内部类对象创建的方式:

​ 如果内部类的权限访问级别是"缺省",且在外部类之外进行对象创建:

​ 后续需要使用外部类对象方式:

​ 外部类类名 外部类对象名 = new 外部类类名();

​ 外部类类名.内部类类名 内部类对象名 = 外部类对象名.new 内部类类名();

​ 后续不用使用外部类对象方式:

​ 外部类类名.内部类类名 内部类对象名 = new 外部类类名().new 内部类类名();

​ 如果内部类的权限访问级别是"私有",且在外部类内部进行对象创建:

​ 内部类类名 内部类对象名 = new 内部类类名(实参);

​ 2.在实例成员内部类不能声明静态成员,如果含有静态成员,需要声明静态成员内部类

​ 3.在外部类的实例成员中可以通过内部类对象访问内部类中的私有成员

​ 4.如果在内部类中访问同名的外部类实例变量,需要使用外部类类名.this.实例变量名;

2)局部内部类

(1)有名字的局部内部类(局部内部类)

​ 学习目的:

​ 1.为"匿名内部类"进行铺垫

​ 2.笔试题

​ 权限访问级别:

​ 只能缺省

​ 注意事项:

​ 1.局部内部类不能被static关键字进行修饰

​ 2.局部内部类在所属方法的外界不能被实例化,只能在所属方法中进行内部类对象的实例化操作

​ 3.局部内部类对象的实例化操作必须在局部内部类声明的后面,否则编译报错

​ 4.局部内部类所属方法的外部类局部变量和该局部内部类中的实例变量或局部变量发生同名的时候,在该局部内部类中无法进行访问

​ 5.局部内部类所属方法的外部类局部变量如果进行重新赋值,则无法在该局部内部类中进行使用

​ 6.局部内部类所属方法的外部类局部变量如果在局部内部类中进行使用,JVM的编译器会自动将其隐式修饰为final(常量)

​ 原因:变量的赋值必须取决于该变量的数据类型

(2)匿名的内部类[重点 !*]

注意:匿名内部类是对于实现类的匿名

​ 学习目的:

​ 1.简化接口的使用步骤

​ 2.学习Lambda表达式的前提条件

​ 权限访问级别

​ 只能缺省

​ 格式:

【修饰符】 class 外部类{ 
 【修饰符】 返回值类型 方法名(【形参列表】){final/abstractclass 内部类{ 
         
     } 
 } 
}

前提:匿名内部类必须继承一个父类或者实现一个父接口

**概述:**它的本质是一个 带具体实现的父类或者父接口的 匿名的 子类对象

**用处:**如果接口的实现类(或者是父类的子类)只需要使用唯一的一次,那这种情况就可以忽略该类的定义,使用匿名内部类。

格式:

new 父类名或者接口名(){ 
    // 方法重写 
    @Override public void method() {
        // 执行语句 
    } 
};


接口名 对象名 = new 接口名称(){

    //覆盖重写所有的抽象方法

}

特点:

1、匿名内部类只能使用唯一的一次

2、 匿名对象在调用方法时只调用唯一的一次

3、枚举类

​ 1.枚举类的含义

​ 针对创建固定数量对象的简化方式

​ 2.枚举类的格式

枚举类的格式:
 *      public enum 枚举类类名 {
 *          对象名1(实参),对象名2(实参),......,对象名n(实参);
 *      }

枚举类的注意事项:

1、如果枚举类没有任何的构造器时,JVM自动给其提供一个私有无参的构造器;一旦有任何构造器,JVM不在提供

2、枚举类中的构造器的访问权限只能是private,不能是其他的访问权限,即使访问权限不写(缺省),JVM 自动补上private

3、枚举类中对象名的这一行必须放在类中的第一行,对象名前面有其他的代码,编译报错

4、如果枚举类中的对象是通过无参构造器进行对象的创建,对象名后面的()可以省略不写

5、枚举类的中的对象被public static final隐式进行修饰,显示修饰时会报错

6、枚举类都默认单一隐式继承Enum类,无法继承其他类和被其他类继承,但可以实现多个接口

7、枚举类无法通过new进行实例化对象的创建,否则编译报错

8、枚举类无法使用super(实参)

day12(6.13)

1、类的初始化

含义:

​ 程序中内容加载到"堆内存"

分类:

​ 实例初始化过程

​ 类初始化过程

无继承关系的实例初始化过程

​ 1.实例成员和构造器代码块,谁在前,优先加载谁

​ 2.构造器中的显式代码

有继承关系的实例初始化过程

​ 1.显式或隐式的super(实参),显式的this(实参)

​ 2.实例成员和构造器代码块,谁在前,优先加载谁

​ 3.构造器中除了super(实参)或this(实参)的显式代码

​ 注意事项:

​ 构造器中一旦含有this(实参),该构造器不会进行实例初始化过程

public class SuperClass {
    int superNum = getSuperNum();

    public int getSuperNum () {
        System.out.println("父类的实例变量显式赋值操作");
        return 10;
    }

    public SuperClass() {
        System.out.println("父类的无参构造器中显式代码");
    }

    {
        System.out.println("父类的构造器代码块");
    }
}
------------------------------------------------
    public class SubClass extends SuperClass {
    int subNum = getSubNum();
    SubClass sc = new SubClass();

    public int getSubNum () {
        System.out.println("子类的实例变量显式赋值操作");
        return 10;
    }

    public SubClass() {
        super();
        System.out.println("子类的无参构造器中显式代码");
    }

    {
        System.out.println("子类的构造器代码块");
    }
}
---------------------------------------------------
    public class InitDemo04 {
    public static void main(String[] args) {
        new SubClass();
    }
}
执行结果:一直循环,直到运行报错
    父类的实例变量显式赋值操作
    父类的构造器代码块
    父类的无参构造器中显式代码
    子类的实例变量显式赋值操作
    .....................
    父类的实例变量显式赋值操作
    父类的构造器代码块
    父类的无参构造器中显式代码
    子类的实例变量显式赋值操作
    .....................

原因:当运行new SubSclass()时,会执行子类当中的无参构造方法中的super(),到达父类的构造方法,先执行父类中实例变量赋值,在执行构造代码块(他们属于同一级,谁在前谁先执行),执行父类无参中显示代码,父类执行完在执行子类;先执行子类实例变量赋值,再执行 SubClass sc = new SubClass();创建对象。每new一次就会调用无参的构造方法,重复上面的步骤一直执行,直到堆内存空间装不下运行报错。

无继承关系的类初始化过程

​ 静态成员和静态代码块谁在前优先加载谁

​ 注意事项:

​ 静态成员随着的类的加载而加载,而且只加载唯一的一次

this关键字的第三种用法

​ 场景:

​ 父类的实例方法中或父类的构造器中

​ 格式:

​ this.实例变量名;

​ this.实例方法名(实参);

​ 作用:

​ 使用多态形式的对象调用实例变量或实例方法

​ 含义:

​ 哪个对象调用了this关键字所在的构造器或实例方法,this关键字就代表所在类型(this关键字所在的类 名)的哪个多态形式对象【this代表的就是父类类型的子类对象】

public class SuperClass {
    int num = 10;

    public SuperClass() {
        this.method();
    }

    public void method () {
        System.out.println("num = " + this.num);
    }
}
------------------------------------------------
public class SubClass extends SuperClass{
int num = 20;

public SubClass() {
    super();
}

public void method () {
    System.out.println("num = " + this.num);
}
}
-------------------------------------------------
public class InitDemo05 {
    public static void main(String[] args) {
        new SubClass();//0
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-37s0Dzii-1658415050655)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\1655108870057.png)]

首先在方法区中加载带main方法的类的字节码文件,他会识别到需要加载的类的字节码,因为SunClass继承SuperClass,所以会先加载父类,再加载子类,里面包括父类和子类的变量和方法,加载完成后进行对象的实例化,new出来的东西在堆内存中,里面包括子类实例成员区和父类引用区,参考字节码文件拷贝一份,为int类型的num进行赋默认值0,将地址值赋给SubClass sc。因为main方法里new SubClass();执行自类当中的无参构造方法,先执行隐式代码(super())进入到父类的无参构造方法,先执行int num = 10;将堆中父类引用区num,改为10,在执行构造器中显示代码块 this.method();会先看看父类中有没有该方法,没有则报错,有的话执行子类重写过的方法,执行method(),打印得到赋默认值的num=0;父类执行完毕再执行int num = 20;更改子类实例成员区num=20。

2、包装类

含义:

​ Java中针对8种基本类型提供对应的引用数据类型

分类:

​ 整数型:

​ Byte(byte),

​ Short(short),

​ Integer(int),

​ Long(long)

​ 浮点型

​ Float(float),

​ Double(double)

​ 布尔型

​ Boolean(boolean)

​ 字符型

​ Character(char)

学习目的:

1.基本类型数据和包装类型数据的转换

2.基本类型数据和字符串数据的转换

3.笔试题

1.拆箱和装箱:

​ 拆箱:将包装类型数据转换成基本类型数据【包装类–>基本类型】(从简原则)

​ 装箱:将基本类型数据转换成包装类型数据【基本类型–>包装类】

1.装箱的方式:

1.包装类型的构造器

2.包装类型的静态方法valueOf(基本类型数据)

3.自动装箱(JDK5.0)推荐

由JVM隐式调用valueOf()完成装箱的过程

	 Integer num3 = 345;

     System.out.println(num3);
2.拆箱的方式:

1.包装类型的实例方法xxxValue()

备注:xxx为对应实例的基本类型单词

2.自动拆箱(JDK5.0)(推荐)

由JVM隐式调用xxxValue()完成拆箱的过程

  int num5 = new Integer(567);

  System.out.println(num5);
2.基本类型数据和字符串类型数据的转换
1.基本类型数据转换成字符串类型数据的方式:

​ 1.字符串连接符

​ 2.先进行装箱,在调用包装类型的实例方法toString()

​ 3.包装类型的静态方法toString(基本类型数据)

​ 4.String类的静态方法valueOf(基本类型数据)(推荐)

2.字符串类型数据转换成基本类型数据的方式:

​ 1.包装类型的构造器,在进行拆箱

​ 2.包装类型的静态方法parseXxx(字符串数据)(推荐)

​ 备注:Xxx为对应实例的基本类型单词

3.注意事项:

​ 1.将字符串类型数据转换成基本类型数据时,字符串中的内容必须在该基本类型的数据范围内,否则发生数据格式化异常(NumberFormatException)

​ 2.Character类并没有提供将字符串类型数据转换成char类型数据的方式

3、包装类的面试题

1.第一种面试题

 		Integer num3 = new Integer(654);
        Integer num4 = new Integer(654);
        System.out.println(num3 == num4);//false==”比较的是引用类型的地址值或基本数据类型的数值
            equals()比较的是引用类型的地址值(没有被覆盖重写下)
   
        Integer num5 = 321;
        Integer num6 = 321;
        System.out.println(num5 == num6);//false

		
        System.out.println("=====================");

        Integer num7 = 123;//Integer类的cache数组的中内容(-128~127)
        Integer num8 = 123;
        System.out.println(num7 == num8);Integer中的值超出(-128~127)这个范围
        System.out.println("=====================");

        Integer num9 = 200;
        int num10 = 200;
        System.out.println(num9 == num10);//从简原则true
        System.out.println(num10 == num9);//true
---------------------------------------------------------------------------------------
 public class IntegerDemo02 {
    public static final Integer num1 = 200;
    public static final Integer num2 = 200;
    public static final Integer num3 = 50;
    public static final Integer num4 = 150;
    public static final Integer num5;
    public static final Integer num6;
    public static final Integer num7;
    public static final Integer num8;

    static {
        num5 = 200;
        num6 = 200;
        num7 = 50;
        num8 = 150;
    }

    public static void main(String[] args) {
        System.out.println(num1 == num2);//false
        System.out.println(num3 == num7);//true

        System.out.println("=====================");
													[从简原则,将Integer转成int进行比较]
        System.out.println(num1 == (num3 + num4));//true
        System.out.println(num5 == (num7 + num8));//true
    }
}

 包装类整型的特殊数据:
 *    在底层缓冲了一个长度为256的对应包装类型的数组,存储-128~127的该类型的对象,如果在范围内,拿数组中对象进行复用,如果不在范围内,new新的该类型的对象.

 包装类字符型的特殊数据:
 *      在底层缓冲了一个长度为128的对应包装类型的数组,存储ASCII码表上的所有字符对象,如果在范围内,拿数组中对象进行复用,如果不在范围内,new新的该类型的对象
 *
 * 包装类布尔型的特殊数据:
 *      在底层缓冲了两个Boolean对象,分别存储truefalse,如果使用拿对应的对象进行复用

day13(6.14)

1、String类(java.lang)

类的特点

​ String 类代表字符串。

重写了toString()方法

类的构造器

public String()

初始化一个新创建的 String 对象,使其表示一个空字符序列

public String(byte[] bytes)

通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 String。

public String(byte[] bytes,int offset,int length)

通过使用平台的默认字符集解码指定的 byte 子数组,构造一个新的 String。

public String(char[] value)

分配一个新的 String,使其表示字符数组参数中当前包含的字符序列。

public String(char[] value,int offset,int count)

分配一个新的 String,它包含取自字符数组参数一个子数组的字符。

public String(String original)

初始化一个新创建的 String 对象,使其表示一个与参数相同的字符序列;

public String(StringBuffer buffer)

分配一个新的字符串,它包含字符串缓冲区参数中当前包含的字符序列。

public String(StringBuilder builder)

分配一个新的字符串,它包含字符串生成器参数中当前包含的字符序列。

String对象的创建方式:

1.字符串字面值赋值

2.通过new创建

字面值赋值和new创建的区别

		String a="aaa";
        String b=new String("aaa");
        String c="aaa";
        System.out.println(a==b); //输出是false,因为new String就是生成了一个新String对象
        System.out.println(a==c); //输出是true,因为当内存中有一个"aaa"的字符串常量的时候,创建String c的时候就不会新建String对象,而是指向a指向的地址
----------------------------------------------------------------------------------------
对于字面量赋值,例如:String str = “abc”;,会先在字符串常量池中寻找是否有"abc"这个字符串,如果存在,则返回引用的实例,否则实例化一个String(“abc”)对象并放入池中再引用

对于new String(“abc”)赋值,则先在池中看是否有这个"abc"的对象,如果没有,则会在池中创建此字符对象,再在堆中创建一个常量池中此 ”abc” 对象的拷贝对象,再引用它,它的value 是 “abc”。如果"abc"这个字符串在常量池中存在,则只在堆中创建对象并引用.

创建字符串对象方式

   		//1.字符串字面值赋值
        String str1 = "abcdefg";
        System.out.println("str1 = " + str1);

        //2.通过无参构造器创建字符串对象
        String str2 = new String();
        System.out.println("str2 = " + str2);
		//相当于"",有别于""

        //3.通过字节数组创建字符串对象
        byte[] bytes = {97,98,99,100,101,102,103};
        String str3 = new String(bytes);
        System.out.println("str3 = " + str3);

        //4.通过以指定范围字节数组创建字符串对象
        String str4 = new String(bytes,3,3);
        System.out.println("str4 = " + str4);

        //5.通过字符数组创建字符串对象
        char[] chars = {'a','b','c','d','e','f','g'};
        String str5 = new String(chars);
        System.out.println("str5 = " + str5);

        //6.通过以指定范围字符数组创建字符串对象
        String str6 = new String(chars,3,3);
        System.out.println("str6 = " + str6);

        //7.通过指定的字符串对象创建该字符串对象的副本
        String str7 = new String("abcdefg");
        System.out.println("str7 = " + str7);

String类的特点及源码分析:

1.在程序中所有的字符串字面值都是String类的实例

2.字符串实例是常量,一旦创建不可以改变

 		 String str1="sdubfdsihb";
        str1.concat("oisafo");//不可变
        System.out.println(str1);

3.字符串实例不可变性的原理:

​ 底层封装了一个被final修饰的数组

4.字符串底层数组的数据类型是:

​ JDK8.0(包含)以前:char[]

​ JDK9.0(包含)以后:byte[]

5.在JDK9.0中,为什么将char[]的底层数组修改为byte[]的底层数组?

时间复杂度

​ 如果底层数组是char[],数组中元素类型是char,进行底层操作时,先将char转换byte,再由byte转换成二进制位

​ 如果底层数组是byte[],数组中元素类型是byte,进行底层操作时,直接将byte转换成二进制位

空间复杂度

底层数组是char[],会根据内码的存储规则进行存储,

​ 当存储字符串内容是"abc"时,每个字符根据内码规则占2个字节,总共3个字符,共占内存6个字节

​ 当存储字符串内容是"尚硅谷"时,每个字符根据内码规则占2个字节,总共3个字符,共占内存6个字节

底层数组是byte[],会根据开发环境的编码规则进行存储,

​ 如果是GBK编码,当存储字符串内容是"abc"时,每个英文字符占2个字节,总共3个字符,共占内存6个字节

​ 如果是GBK编码,当存储字符串内容是"尚硅谷"时,每个汉字字符占2个字节,总共3个字符,共占内存6个字节

​ 如果是utf8编码,当存储字符串内容是"abc"时,每个英文字符占1个字节,总共3个字符,共占内存3个字节

​ 如果是utf8编码,当存储字符串内容是"尚硅谷"时,每个汉字字符占3个字节,总共3个字符,共占内存9个字节

6.String实例为什么可以存储中文?

​ JVM的底层和String类的底层都有UTF-16编码完成,仅支持Unicode码表中的中文

7.String实例的极限长度是多少?

​ 最短字符串长度为0,最长字符串长度为65535

8.""的null的区别

​ “”,字面值常量,字符串对象
​ ""可以调用方法

​ null字面值常量,引用类型的默认值

类的方法

1、判断功能方法

public boolean equals(Object anObject)

​ 将此字符串与指定的对象比较

public boolean equalsIgnoreCase(String anotherString)

​ 将此 String 与另一个 String 比较,不考虑大小写。

public boolean contains(CharSequence s)

​ 当且仅当此字符串包含指定的 char 值序列时,返回 true。

public boolean startsWith(String prefix)

​ 测试此字符串是否以指定的前缀开始。

public boolean endsWith(String suffix)

​ 测试此字符串是否以指定的后缀结束。

public boolean isEmpty()

​ 当且仅当 length() 为 0 时返回 true。

public int compareTo(String anotherString) 按字典顺序比较两个字符串。

2、获取功能方法

public char charAt(int index)

​ 返回指定索引处的 char 值。

public int indexOf(String str)

​ 返回指定子字符串在此字符串中第一次出现处的索引。

public int lastIndexOf(String str)

​ 返回指定子字符串在此字符串中最右边出现处的索引。

public int length()

​ 返回此字符串的长度。

public String substring(int beginIndex)

​ 返回一个新的字符串,它是此字符串的一个子字符串

public String substring(int beginIndex,int endIndex)

​ 返回一个新字符串,它是此字符串的一个子字符串。

3、转换功能方法

public String concat(String str)

​ 将指定字符串连接到此字符串的结尾。

public byte[] getBytes()

​ 使用平台的默认字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。

public char[] toCharArray()

​ 将此字符串转换为一个新的字符数组。

public String replace(CharSequence target,CharSequence replacement)

​ 使用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串。

public String toLowerCase()

​ 使用默认语言环境的规则将此 String 中的所有字符都转换为小写。

public String toUpperCase()

​ 使用默认语言环境的规则将此 String 中的所有字符都转换为大写。

4、分割功能方法

public String[] split(String regex)【分割字符串,并返回字符数组】

​ 根据给定的"规则"的匹配拆分此字符串。

String类split()的注意事项:

1.在根据指定的规则进行分割后,指定规则会消失

2.如果给的规则不在字符串的最后,连续的规则进行多次分割,分割后的内容是长度为0的字符串

3.如果给的规则在字符串的最后,在最后的规则进行分割时忽略不计

StringBuffer类和StringBuilder类的相同点和不同点:

相同点

​ 1.二者拥有相同的父类

​ 2.与String不同,二者是可变的字符序列

不同点:

​ 线程安全性不同:

​ StringBuffer类是线程安全的,适用于多线程程序,如果在单线程中进行使用,相比StringBuilder类低很多

​ StringBuilder类是线程不安全的,适用于单线程程序,如果在多线程中进行使用,需要手动添加线程安全

StringBuilder类

类的特点

​ 1.一个可变的字符序列。

​ 原因:底层数组没有被final进行修饰

​ 2.StringBuilder类是线程不安全的,适用于单线程程序,如果在多线程中进行使用,需要手动添加线程安全

类的构造器

public StringBuilder()

​ 构造一个不带任何字符的字符串生成器,其初始容量为 16 个字符。

类的方法

public StringBuilder append(String str)

​ 将指定的字符串追加到此字符序列

public StringBuilder insert(int offset,String str)

​ 将字符串插入此字符序列

public StringBuilder reverse()

​ 将此字符序列用其反转形式取代。

StringBuilder类的源码分析:

1.StringBuilder类底层数组的初始容量是多少

初始容量是多少取决于创建对象时所选择的构造器

​ (1)StringBuilder()

​ 初始容量:16

​ (2)StringBuilder(CharSequence seq)【CharSequence 是一个父类接口,他的实现类有String、 StringBuilder、StringBuffer,所以seq传的可以是实现类对象】

​ 初始容量:参数长度+16

​ (3)StringBuilder(int capacity)

​ 初始容量:自定义

​ (4)StringBuilder(String str)

​ 初始容量:参数长度+16

2.StringBuilder类底层数组的扩容原理(规则)

​ JDK8.0(包含)以后:

​ (原来数组长度 << 1 ) + 2

​ JDK7.0(包含):

​ 原来数组长度 * 2 + 2;

​ JDK6.0(包含)以前:

​ (原来数组长度 + 1) * 2;

3.StringBuilder类底层数组扩容的时候,为什么+2?

为了防止通过自定义初始容量数组传0的特殊情况

day14(6.15)

1、String的面试题

  1. 比较字符串的地址值

    字符串的字面值常量存储于内存中的"字符串常量池"

 String str3 = "HelloWorld";  

//字面值赋值
 String str7 = "Hello";
 String str8 = "World";false

System.out.println(str3 == (str7 +str8));
//常量池 == 堆

//常量优化机制是针对常量或final修饰的变量,变量不会触发该机制
//‘+’底层还是使用了StringBuilder的append()方法

System.out.println(str3 == "Hello" + "World");//true
//由于str3在常量池中已经有了,在对后面字面值进行拼接后,会在常量池中寻找,找到了就将有的地址值直接指向拼接后的字面值,比较地址值。

常量优化机制:

在给一个变量赋值的时候,如果“=”的右边全部是常量(包括final关键字定义的常量在内)那么在编译阶段会把右边的结果赋值给左边的变量。

常量优化机制一定是针对常量的,如果有变量参与那么就不会触发常量优化机制。 变量被final关键字修饰,则作用相当于常量。

直接声明初始化的自定义常量会被jvm识别为“字面值常量”

先声明后初始化不会被当成字面值常量

 public static final String str1 = "HelloWorld"; 
 public static final String str3 = "Hello";
 public static final String str4 = "World";
 public static final String str5;
 public static final String str6;
 public static final String str7;
 public static final String str8;
static {
        str5 = "HelloWorld";
        str6 = "HelloWorld";
        str7 = "Hello";
        str8 = "World";
    }
//被final进行修饰时直接声明初始化时,jvm会将自定义常量识别为字面值常量,所以在两个字面值进行+操作,是先将拼接提前至编译时期,str1先在常量池中寻找,找不到就进行创建。在str3+str4,在常量池中找到了,将找到的地址值赋给了字面值
System.out.println(str1 == (str3 + str4));//true常量池 == 常量池

        System.out.println(str5 == (str7 + str8));false
        //final关键字修饰的变量必须初始化和赋值同时进行,
        // jvm才会把他当成字面值常量

3、

 String str1 = "HelloWorld";
        //String对象数量和位置:1个(常量池)

        String str2 = new String("HelloWorld");
        //String对象数量和位置:2个(常量池1个,堆内存1个)

        String str3 = "H" + "e" + "l" + "l" + "o" + "W" + "o" + "r" + "l" + "d";
        //String对象数量和位置:1个(常量池)

        String s1 = "H";
        String s2 = "e";
        String s3 = "l";
        String s4 = "l";
        String s5 = "o";
        String s6 = "W";
        String s7 = "o";
        String s8 = "r";
        String s9 = "l";
        String s10 = "d";

        String str4 = s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10;
        //String对象数量和位置:8个(常量池7个,堆内存1个)

32、异常

异常的好处:

  •  学习异常,了解异常,处理异常,从而保证程序可以正常的运行
    
1.Throwable类(java.lang)

​ 类的特点

​ Throwable 类是 Java 语言中所有错误(Error)或异常(Exception)的父类

​ 错误(Error):Java程序出现了不可以通过后期自动补救方法进行解决的错误,只能改写代码

​ 异常(Exception):Java程序出现可以通过后期自动补救方法进行解决的错误,不用后期改写代码,提前做好准备

​ 类的构造器

public Throwable()

​ 构造一个将 null 作为其详细消息的新 throwable。

public Throwable(String message)

​ 构造带指定详细消息的新 throwable。

​ 类的方法

public void printStackTrace()

​ 将此 throwable 及其追踪输出至标准错误流

public String getMessage()

​ 返回此 throwable 的详细消息字符串。

2.异常的分类:

​ 运行时异常

​ 编译时异常

1.运行时异常:

​ 代码没有任何语法格式错误,在运行代码的时候出现了非正常的情况导致JVM停止运行

​ 位置:

​ RuntimeException类及其子类

​ 常见:

​ 空指针异常(NullPointerException)

​ 索引越界异常(IndexOutOfBoundsException)

​ 类型转换异常

​ 数据格式化异常

​ …

2.编译时异常

​ 代码没有任何语法格式错误,在编译代码的时候出现了编译报错

位置:

Exception类及其子类(RuntimeException类及其子类除外)

区别:

处理方式不同

3.异常处理:

​ Java程序中所有的异常必须进行处理

分类:

​ 异常声明处理:将异常问题交给调用者进行解决

​ 异常捕获处理:将异常问题自己独立进行解决(推荐)

关键字:

throws,try,catch,finally

1.运行时异常的处理方案:

​ 1.异常声明处理

​ 2.异常捕获处理

​ 3.无需程序员进行处理

如果程序员不针对运行时异常处理JVM主动进行处理:

(1)终止JVM

(2)终止的同时,将异常信息进行打印

2.编译时异常的处理方案:

​ 1.异常声明处理

​ 2.异常捕获处理

1:异常声明处理

含有异常的方法不进行异常的处理,将异常交给其调用者进行解决

格式:

修饰符 返回类型 方法名 () throws 异常类名1,异常类名2,......,异常类名n {}

异常声明处理的注意事项:继承

继承关系出现在抛出的异常对象中

​ 1.进行异常声明时无需关注声明异常子父类的前后顺序

​ 2.进行异常声明时可以直接声明异常的父类

继承关系出现在异常所在的类中

​ 1.异常在父类被重写的方法中,子类重写该方法时无需理会该异常

​ 2.异常在子类重写后的方法中,处理方案只有唯一的一种,异常的捕获处理(不能够进行声明异常)

2.异常捕获处理

捕获到程序出现的异常,针对异常进行合理的解决方案,从而保证程序可以正常运行

格式1:(其中catch可以多个)
try {

可能出现异常的代码

} catch (异常类型 对象名) {

出现该异常的解决方案

}
----------------------------------------------
格式2:

try {

可能出现异常的代码

} catch (异常类型 对象名) {

出现该异常的解决方案

} finally {

必须执行的代码

}
----------------------------------------------
格式3:
(JDK7.0):
 针对关闭资源对象异常捕获处理的优化【可以不用编写资源释放,程序自行关闭,不管会不会异常,都会释放资源】

try (
//如Scanner sc=new Scanner();
  //................
类名 对象名 = new 类名();

......

) {

可能出现异常的代码

} catch () {

出现该异常的解决方案

}

3.异常捕获的注意事项:

继承关系出现在抛出的异常对象中

​ 1.分别捕获,分别处理

​ 场景:适用于每个异常都有特有的解决方案

​ 2.一次捕获,分别处理

​ 场景:每个异常存在关联性,解决方案各自不同

​ 注意:需要将异常子类的处理放在父类上面(前面)

​ 3.一次捕获,一次处理

​ 场景:每个异常存在关联性,解决方案相同

4.关于异常的关键字
1.throw关键字:

抛出指定的异常对象

格式:

throw new 异常类名(实参);throw new Exception();
2.finally关键字

在进行异常捕获声明时必须执行的代a码

finally关键字的注意事项

  •  在try...catch...finally里面尽量避免使用return
    
5、自定义异常:

自定义运行时异常

自定义编译时异常(重点)

1.自定义运行时异常:

​ 1.创建自定义运行时异常类

​ 2.将该类继承RuntimeException类

​ 3.根据父类生成合适的构造器(至少包含无参和String参数的构造器)

2.自定义编译时异常:

​ 1.创建自定义编译时异常类

​ 2.将该类继承Exception类

​ 3.根据父类生成合适的构造器(至少包含无参和String参数的构造器)

day15(6.17)—day16(6.18)

1、日期时间类

第一代(JDK1.0):Date类,DateFormat类,SimpleDateFormat类

第二代(JDK1.1):Calendar类

第三代(JDK8.0):LocalDate类,LocalTime类,LocalDateTime类,DateTimeFormatter类

1.Date类 (java.util)

表示特定的瞬间,精确到毫秒

构造器

  public Date()

  ​		分配 Date 对象并初始化此对象,以表示分配它的时间(精确到毫秒)。

  * public Date(long date)

  分配 Date 对象并初始化此对象,以表示自从标准基准时间(称为“历元(epoch)”,

  即 1970 年 1 月 1 日 00:00:00 GMT)以来的指定毫秒数。

  * public Date(int year,int month,int date,int hrs,int min,int sec)

  分配 Date 对象,并初始化此对象,以表示本地时区中由 year、month、date、hrs、min 和 sec 参数指定的秒的开始瞬间。

  * public Date(int year,int month,int date,int hrs,int min)

  分配 Date 对象,并初始化此对象,以表示本地时区中由 year、month、date、hrs、min参数指定的秒的开始瞬间。

  * public Date(int year,int month,int date)

  分配 Date 对象,并初始化此对象,以表示本地时区中由 year、month、date参数指定的秒的开始瞬间。

常用方法

public long getTime()

  •          返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数。
    
Date与毫秒值的转换
	Date中的getTime()方法可以返回当前日期对象的毫秒值
	Date的构造器可以将毫秒值转为Date对象

Date对象的格式化和解析:

1.格式化:Date对象==>文本(String)
Date对象的格式化

​	1.创建Date对象

​	2.通过给定的格式创建SimpleDateFormat对象

​	3.进行格式化操作
----------------------------------------------
public static void main(String[] args) {
       //1.创建Date对象
       Date date = new Date();
      //2.通过给定的格式创建SimpleDateFormat对象
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 EEE HH时mm分ss秒SSS毫秒");
       //3.进行格式化操作
       String format = sdf.format(date);
       System.out.println(format);
    }
}
2.解析:文本(String)==>Date对象
 Date对象的解析:

 *      1.声明并初始化日期时间文本
     
 *      2.通过给定的格式创建SimpleDateFormat对象
     
 *      3.进行解析操作
 -----------------------------------------------------------------
  public static void main(String[] args) throws ParseException {
        //1.声明并初始化日期时间文本
        String str = "2022年06月17日 星期五 09时23分16秒659毫秒";

        //2.通过给定的格式创建SimpleDateFormat对象
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 EEE HH时mm分ss秒SSS毫秒");

        //3.进行解析操作
        Date parse = sdf.parse(str);

        System.out.println(parse);
    }
}
格式化是将一个date对象转换成string文本
解析是将一个string文本转换成date对象
DateString
    1、创建date对象
    2、通过给定的格式创建simpleDateFormat
    3、进行格式化操作(format)
    
StringDate对象
    1、声明并初始化日期时间文本
    2、通过给定的格式创建simpleDateFormat对象
    3、进行解析操作(parse)
2.DateFormat类 (java.text)

​ 针对Date对象进行格式化和解析操作工具类的父类

类的构造器

​ 该类是抽象类

类的方法

public final String format(Date date)

将一个 Date 格式化为日期/时间字符串。

​ date - 要格式化为时间字符串的时间值。
​ 返回:
​ 已格式化的时间字符串。

public Date parse(String source)

从给定字符串的开始解析文本,以生成一个日期。该方法不使用给定字符串的整个文本。

​ source - 要解析的日期/时间字符串
​ pos - 输入时,是开始进行解析的位置;输出时,是解析终止的位置,如果解析失败,则为开 始位置。
​ 返回:
​ 一个 Date,如果无法解析输入,则为 null

SimpleDateFormat类(java.text)

​ 针对Date对象进行格式化和解析操作的工具类

类的构造器

​ public SimpleDateFormat(String pattern)

​ 用给定的模式和默认语言环境的日期格式符号构造 SimpleDateFormat。

​ 参数:
​ pattern - 描述日期和时间格式的模式

​ 模式:区分大小写

​ y 年

​ M 月

​ d 日

​ H 时

​ m 分

​ s 秒

	 “yyyy-MM-dd-HH-mm-ss”

​ “yyyy年MM月dd日HH时mm分ss秒”

//1、创建SimpleDateFormat对象,构造方法中传入指定模式

SimpleDateFormat sm=new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");

//2、调用SimpleDateFormat中的方法format,将Date转化为字符串文本

        Date date=new Date();

        String text=sm.format(date);

        System.out.println(date);

        System.out.println(text);
3.Calendar类(java.util)

针对日期时间操作的第二代工具类

类的构造器:该类为抽象类

类的方法

public static Calendar getInstance()

使用默认时区和语言环境获得一个日历。

public int get(int field)

返回给定日历字段的值。

4.LocalDateTime类(java.time)

不能够创建对象,只能通过LocalDateTime ldt = LocalDateTime.now();

​ 针对日期时间操作的第三代工具类

类的构造器

​ 构造器私有化【构造器私有化就代表着不能创建该类的对象(不能new)】

类的方法

public static LocalDateTime now()

从默认时区中的系统时钟中获取当前日期时间。

public int getYear()

获取年度字段。

public int getMonthValue()

从1到12得到一个月的时间字段。

public int getDayOfMonth()

获取月字段的一天。

public int getHour()

获取天字段的时间。

public int getMinute()

获取小时字段的分钟。

public int getSecond()

获取第二个分钟字段。

public static LocalDateTime parse(CharSequence text,DateTimeFormatter formatter)

获得 LocalDateTime实例从使用特定格式的文本字符串。

5.DateTimeFormatter类(java.time.format)【底层final修饰,不能创建对象以及不能被继承】

​ 针对第三代日期时间类进行格式化和解析操作的工具类

创建是通过DateTimeFormatter dtf=DateTimeFormatter.ofPattern(“yyyy年MM月dd日HH时”);

类的构造器:构造器私有化

类的方法

public static DateTimeFormatter ofPattern(String pattern)

创建一个格式化程序使用指定的模式。

public String format(TemporalAccessor temporal)

使用此格式化程序格式的日期时间对象。

public TemporalAccessor parse(CharSequence text)

充分分析文本产生的时空对象

1.LocalDateTime对象的格式化

​ 1.获取LocalDateTime对象

​ 2.获取DateTimeFormatter对象,并指定格式

​ 3.进行格式化操作

2.LocalDateTime对象的解析

​ 1.声明并初始化日期时间文本

​ 2.获取DateTimeFormatter对象,并指定格式

​ 3.进行解析操作

localdatetime对象进行格式化操作:
1、获取localdatetime对象
2、获取datetimeformatter对象,并指定格式
3、进行格式化操作

localdatetime对象进行解析操作
1、声明并初始化日期时间文本
2、获取datetimeformatter对象,并指定格式(datetimeformatter中的ofpattern方法)
3、进行解析操作

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P3k3rYr4-1658415050657)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\1655467850843.png)]

在解析时所写的月份必须要在两位,不然会报错,或者把MM月改成M月,系统会自动输出两位

  	//1.声明并初始化日期时间文本
    String str = "2022年06月17日 09时09分09秒009毫秒";

    //2.获取DateTimeFormatter对象,并指定格式
 DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒SSS毫秒");

    //3.进行解析操作
      LocalDateTime parse = LocalDateTime.parse(str, dtf);

    System.out.println(parse);

2、File (java.io)

​ 文件和目录路径名的抽象表示形式

类的构造器

public File(String pathname)

通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。

类的方法

public String getAbsolutePath()

返回此抽象路径名的绝对路径名字符串

public String getPath()

将此抽象路径名转换为一个路径名字符串。

public String getName()

返回由此抽象路径名表示的文件或目录的名称。

public long length()

返回由此抽象路径名表示的文件的长度。

public boolean exists()

测试此抽象路径名表示的文件或目录是否存在。

public boolean isDirectory()

测试此抽象路径名表示的文件是否是一个目录。

public boolean isFile()

测试此抽象路径名表示的文件是否是一个标准文件。

public boolean createNewFile()

当且仅当不存在具有此抽象路径名指定名称的文件时,不可分地创建一个新的空文件。

public boolean mkdir()

创建此抽象路径名指定的目录。

public boolean mkdirs()

创建此抽象路径名指定的目录,包括所有必需但不存在的父目录。

public File[] listFiles()

返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件。

绝对路径和相对路径

  •  绝对路径:从盘符开始一直到所表示的文件或目录,是一个完整的路径
    
  •  构造路径:构造器中的路径,既可以是绝对路径,也可以是相对路径
    
  •  相对路径:单独的文件名或目录名,是一个不完整的路径,相对所在项目路径而言,是一个变化的路径(推荐)
    
 		按照固定格式遍历多级文件夹

		 *      文件:路径\文件名.后缀名
		 *      目录:路径\目录名
public static void main(String[] args) {
        //创建File对象
        File file = new File("D:\\develop\\Java\\jdk1.8.0_202");

        print(file);
    }

    public static void print (File dir) {
        //针对dir进行非空校验
        if (dir == null) {
            System.out.println("该路径不是目录路径,无法进行多级遍历");
            return;
        }

        //获取当前路径下所有单级文件和单级目录
        File[] files = dir.listFiles();

        //遍历数组
        for (int i = 0; i < files.length; i++) {
            //获取当前遍历到的路径
            File file = files[i];

            //针对当前遍历到的路径进行文件校验
            if (file.isFile()) {
                System.out.println("文件:" + file);
            } else {
                System.out.println("目录:" + file);
                print(file);
            }
        }
    }

3、IO流

IO流:

数据的输入输出

​ 输入:数据以"字节"或"字符"为单位从"其它硬件设备"到"内存"的流动

​ 输出:数据以"字节"或"字符"为单位从"内存"到"其它硬件设备"的流动

四大基本流:

​ 字节输入流(InputStream抽象父类)

​ 字节输出流(OutputStream抽象父类)

​ 字符输入流(Reader抽象父类)

​ 字符输出流(Writer抽象父类)

功能分类:

​ 文件流

​ 文件字节流

​ 文件字节输入流

​ 文件字节输出流

​ 文件字符流

​ 文件字符输入流

​ 文件字符输出流

​ 缓冲流

​ 缓冲字节流

​ 缓冲字节输入流

​ 缓冲字节输出流

​ 缓冲字符流

​ 缓冲字符输入流

​ 缓冲字符输出流

​ 转换流

​ 转换输入流

​ 转换输出流

​ 对象流

​ 对象输入流

​ 对象输出流

​ 标准流

​ 标准输入流

​ 标准输出流

1、字节流
1.FileInputStream类(文件字节输入流)(java.io)

​ 针对文件以字节为单位进行读取操作的工具类

类的构造器

public FileInputStream(File file)

通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。

public FileInputStream(String name)

通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。

类的方法

public void close()

关闭此文件输入流并释放与此流有关的所有系统资源。

public int read()

从此输入流中读取一个数据字节

public int read(byte[] b)

从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。

2.FileOutputStream类(文件字节输出流)(java.io)

​ 针对文件以字节为单位进行写入操作的工具类

类的构造器

public FileOutputStream(File file)

创建一个向指定 File 对象表示的文件中写入数据的文件输出流

public FileOutputStream(File file,boolean append)

创建一个向指定 File 对象表示的文件中写入数据的文件输出流。

public FileOutputStream(String name)

创建一个向具有指定名称的文件中写入数据的输出文件流。

public FileOutputStream(String name,boolean append)

创建一个向具有指定 name 的文件中写入数据的输出文件流。

类的方法

public void close()

关闭此文件输出流并释放与此流有关的所有系统资源。

public void write(int b)

将指定字节写入此文件输出流。

public void write(byte[] b)

将 b.length 个字节从指定 byte 数组写入此文件输出流中。

public void write(byte[] b,int off,int len)

将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流。

文件的回车换行

​ \r\n

​ 回车:将光标移动到当前行的行首

​ 换行:将光标移动到下一行相同的位置

2、字符流

文件字节流的优势:

​ 适用于进行图片,视频,音频等

文件字节流的弊端:

​ 操作文件(编码)>字节(解码)>写入(编码)==>使用文件(解码)

​ 编码和解码的过程中出现了数据的转换问题,可能出现乱码情况

文件字符流目的

​ 在读写文本文件时,涉及中文字符不进行中文和字节的转换

1.FileReader类(文件字符输入流)

针对文件以字符为单位进行读取操作的工具类

类的构造器

​ public FileReader(File file)

​ 在给定从中读取数据的 File 的情况下创建一个新 FileReader

​ public FileReader(String fileName)

​ 在给定从中读取数据的文件名的情况下创建一个新 FileReader。

类的方法

​ public void close()

​ 闭该流并释放与之关联的所有资源。

​ public int read()

​ 读取单个字符。

​ public int read(char[] cbuf)

​ 将字符读入数组

2.FileWriter类(文件字符输出流)

针对文件以字符为单位进行写入操作的工具类

类的构造器

public FileWriter(File file)

根据给定的 File 对象构造一个 FileWriter 对象。

public FileWriter(File file,boolean append)

根据给定的 File 对象构造一个 FileWriter 对象。

public FileWriter(String fileName)

根据给定的文件名构造一个 FileWriter 对象。

public FileWriter(String fileName,boolean append)

根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。

类的方法

public void close()

关闭此流,但要先刷新它。

public void write(int c)

写入单个字符。

public void write(char[] cbuf)

写入字符数组。

public void write(char[] cbuf,int off,int len)

写入字符数组的某一部分。

public void write(String str)

写入字符串。

public void write(String str,int off,int len)

写入字符串的某一部分。

public void flush()

刷新该流的缓冲。

文件字符输出流注意事项:

​ close():关闭并刷新,关闭流后不能再进行使用

​ flush():刷新该流,将内存缓冲区中的写入内容写入到指定文件中,后续可以继续使用该流

3、缓冲流 (java.io)
1.BufferedInputStream类(缓冲字节输入流)

​ 针对另外一个字节输入流添加缓冲区使之具有高效读取功能的工具类

类的构造器

​ public BufferedInputStream(InputStream in)

​ 创建一个 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。

类的方法

​ 详见文件字节输入流方法

2.BufferedOutputStream类(缓冲字节输入流)

​ 针对另外一个字节输出流添加缓冲区使之具有高效写入功能的工具类

类的构造器

​ public BufferedOutputStream(OutputStream out)

​ 创建一个新的缓冲输出流,以将数据写入指定的底层输出流。

类的方法

​ 详见文件字节输出流方法

		 //创建文件字节输入输出流对象
        FileInputStream fis = new FileInputStream("G:\\jdk-8u202-windows-x64.exe");
        FileOutputStream fos = new FileOutputStream("G:\\jdk.exe");
        //创建缓冲字节输入输出流对象
        BufferedInputStream bis = new BufferedInputStream(fis);
        BufferedOutputStream bos = new BufferedOutputStream(fos);
----------------------------------------------------------------------------
    
3.BufferedReader类(缓冲字符输入流)

​ 针对另外一个字符输入流添加缓冲区使之具有高效读取功能的工具类

类的构造器

​ public BufferedReader(Reader in)

​ 创建一个使用默认大小输入缓冲区的缓冲字符输入流。

类的方法

​ public String readLine()

​ 读取一个文本行。

4.BufferedWriter类(缓冲字符输出流)

​ 针对另外一个字符输出流添加缓冲区使之具有高效写入功能的工具类

类的构造器

​ public BufferedWriter(Writer out)

​ 创建一个使用默认大小输出缓冲区的缓冲字符输出流。

类的方法

​ public void newLine()

​ 写入一个行分隔符。

4、转换流

字符集:

​ 含义:十进制数字和字符进行一一对应的码表

常见:

​ ASCII字符集

​ Unicode字符集

​ GB字符集

​ ISO-8859字符集

字符编码:

​ 含义:以某种存储规则将存储字节和字符进行转换关系

常见:

​ ASCII字符集:

​ ASCII-128字符编码

​ ASCII-256字符编码

​ Unicode字符集

​ UTF-8字符编码

​ UTF-16字符编码

​ UTF-32字符编码

​ GB字符集

​ GB2312字符编码

​ GBK字符编码

​ GB18030字符编码

​ ISO-8859字符集

​ ISO-8859-1字符编码

​ ISO-8859-2字符编码

​ …

转换流的学习目的

​ 在进行编码或者解码的过程中可以指定编码或解码的方式

1.InputStreamReader类(转换输入流)

​ InputStreamReader 是字节流通向字符流的桥梁

类的构造器

public InputStreamReader(InputStream in)

创建一个使用默认字符集的 InputStreamReader。

public InputStreamReader(InputStream in,String charsetName)

创建使用指定字符集的 InputStreamReader。

类的方法

​ 详见文件字符输入流方法

2.OutputStreamWriter类(转换输出流)

​ OutputStreamWriter 是字符流通向字节流的桥梁:

类的构造器

public OutputStreamWriter(OutputStream out)

创建使用默认字符编码的 OutputStreamWriter。

public OutputStreamWriter(OutputStream out,String charsetName)

创建使用指定字符集的 OutputStreamWriter。

类的特点

​ 详见文件字符输出流方法

//编码解码过程:本地文件(uft8编)==>上传读取(uft8解)==>保存写入(gbk编)==>打开文件(gbk解)
    private static void method03() throws IOException {
        //创建转换输入输出流对象
        InputStreamReader isr = new InputStreamReader(new FileInputStream("G:\\20220526\\day16\\resources\\utf8.txt"));
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("G:\\20220526\\day16\\resources\\utf8_gbk.txt"),"gbk");

        //进行读写操作
        int len;
        char[] chars = new char[8192];
        while ((len = isr.read(chars)) != -1) {
            osw.write(chars,0,len);
        }

        //关闭资源
        osw.close();
        isr.close();
    }
5、对象流(序列化和反序列化)

​ 序列化:

​ 反序列化:

1.ObjectOutputStream类(对象输出流,序列化过程)

​ 针对对象和基本数据类型进行输出的字节流工具类

类的构造器

public ObjectOutputStream(OutputStream out)

创建写入指定 OutputStream 的 ObjectOutputStream。

类的方法

public final void writeObject(Object obj)

将指定的对象写入 ObjectOutputStream。

2.Serializable接口(java.io)

接口的特点

​ 类通过实现 java.io.Serializable 接口以启用其序列化功能

接口的方法

​ 没有方法

3.ObjectInputStream类(对象输入流,反序列化)

​ ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化

类的构造器

​ public ObjectInputStream(InputStream in)

​ 创建从指定 InputStream 读取的 ObjectInputStream。

类的方法

​ public final Object readObject()

​ 从 ObjectInputStream 读取对象。

4.对象流的注意事项:

1.实现序列化操作,必须让对象所对应的类实现Serializable,否则会发生NotSerializableException

2.如果需要通过序列化操作多个对象,需要将多个对象存储"集合容器"中,在序列化和反序列化过程中尽量操作一个序列化对象

3.在实现序列化和反序列化过程中,针对序列化类不能做任何修改

4.在实现序列化和反序列化过程中,针对序列化文件不能做任何修改

5.在实现序列化和反序列化过程中,如果对象的某些属性不需要实现序列化操作,可以transient关键字进行修饰符

5.transient关键字:

含义:

​ 瞬态的

作用:

​ 被transient关键字修饰的属性不可以被序列化

格式:

​ 修饰符 transient 数据类型 变量名;

6、标准流

标准输入流

System.in

标准输出流

System.out

PrintStream类

​ 针对各种类型的数据值进行方法打印操作的工具类

类的构造器

​ public PrintStream(OutputStream out)

​ 创建新的打印流。此流将不会自动刷新。

​ public PrintStream(String fileName)

​ 创建具有指定文件名称且不带自动行刷新的新打印流。

7、IO流的关闭规则:

1.先开后关,后开先关

2.IO流的关闭资源建议写在finally代码块中或者JDK7.0新try…catch语法中

day17(6.20)

1、集合(集合都是现在util包下)

集合和数组的区别

​ 1、数组长度不可变,集合长度可变

​ 2、数组可以存储基本数据类型和引用数据类型,而集合只能存储引用数据类型(对象)

集合概念

​ 存储同一种数据类型的多个对象的容器

​ 分类:

​ 单列集合(Collection)

​ 存储元素时以“个”为单位

​ 双列集合(Map)

​ 存储元素时以“对”为单位

2、collection接口

​ List接口

​ ArrayList 类

​ Vector 类

​ LinkedList 类

​ Set接口

​ HashSet 类

​ LinkedHashSet 类

​ TreeSet 类

Map接口分类

​ HashMap 类

​ LinkedMap 类

​ TreeMap 类

​ Hashtable 类

​ Properties 类

1.collection接口(java.util)

​ 单列集合的顶级接口

接口的方法:

​ boolean add(E e)

  • 确保此 collection 包含指定的元素(可选操作)。

void clear()

  • 移除此 collection 中的所有元素(可选操作)。

boolean contains(Object o)

  • 如果此 collection 包含指定的元素,则返回 true。

boolean isEmpty()

  • 如果此 collection 不包含元素,则返回 true。

Iterator iterator()

  • 返回在此 collection 的元素上进行迭代的迭代器。

boolean remove(Object o)

  • 从此 collection 中移除指定元素的单个实例,如果存在的话(可选操作)。

int size()

  •          返回此 collection 中的元素数
    
2.collection集合的面试题

使用多态的形式创建Collection集合对象,而多态的特点主要和方法有关,当多态的对象调用方法,先虚调用父类或父接口中的方法,如果

有执行子类或实现类重写后的方法,如果没有,编译报错,在打印集合对象时,其实就是调用集合多态对象的toString(),

调用该toString()会先需调用父接口Collection中的toString(),通过查看源码发现Collection接口及其父接口都没有toString(),

代码应该编译报错,但实际上并没有报错,为什么?

所有的接口都继承Object类抽象形式或者所有的接口都含有Object类中方法的抽象形式

 Collection coll = new ArrayList();
        //往集合中添加元素
        coll.add("abc");
        //打印集合
System.out.println(coll.toString());

3、迭代器(Iterator)

​ Collection集合通用的遍历方式

Iterator接口

​ 对collection进行迭代的迭代器

接口中的方法

​ boolean hasNext()

​ 如果仍有元素可以迭代,则返回 true

​ E next()

​ 返回迭代的下一个元素。

迭代器的注意事项

​ 1、迭代器对象只能进行唯一的一轮迭代,如果多轮迭代,需要每轮重新获取迭代器对象,否则发生没有元素异常NoSuchElementException

​ 2.在使用迭代器的过程中,不能针对集合中的元素做任何增删操作,否则发生并发修改异常ConcurrentModificationException

增强for循环(JDK5.0)

注意:

​ 针对迭代器使用的简化方式(本质就是迭代器)

格式:

for (元素数据类型 元素名 : 容器名) {


}
--------------------------------------
//使用增强for进行遍历
  for (Object obj : coll) {
      
    System.out.println(obj);
       
  }

增强for循环注意事项:

​ 1、增强for循环的底层其实是一个迭代器对象

​ 2、在使用时,不能够针对集合中的元素做任何增删操作,否则发生“并发修改异常(ConcurrentModificationException)”

​ 3、增强for既可以遍历集合也能遍历数组,但不推荐使用

​ 原因:使用增强for遍历引用类型数组时,底层会将引用类型数组先转成collection集合对象;使用增强for遍历基本类型数组时,底层会将增强for转换为普通for进行遍历

collection集合遍历的四种方式:

​ 1、迭代器

​ 2、增强for循环

​ 3、stream流的forEach

​ 4、forEach()

 Collection coll=new ArrayList();
        coll.add("韩");
        coll.add("俊");
        coll.add("豪");

        //迭代器
        Iterator iterator=coll.iterator();
        while (iterator.hasNext()) {
            Object next =  iterator.next();
            System.out.println(next);
        }
        System.out.println("=============");
        //增强for循环
        for(Object o:coll){
            System.out.println(o);
        }
        System.out.println("=============");
        //Stream流
        Stream stream=coll.stream();
        stream.forEach(new Consumer() {
            @Override
            public void accept(Object o) {
                System.out.println(o);
            }
        });
        System.out.println("==============");
        //forEach
        coll.forEach(new Consumer() {
            @Override
            public void accept(Object o) {
                System.out.println(o);
            }
        });

4、泛型(jdk5.0)

​ 未知的数据类型

​ <泛型>

注意事项:

​ 1、如果使用泛型,必须在进行类、接口、方法声明时进行泛型的声明

​ 2、如果使用泛型,必须在合适的实际确定泛型,否则JVM默认确定为Object类型

好处:更好的限制数据类型的使用

1.泛型的基础应用
1.含有泛型的接口
声明格式:

	public interface 接口名<泛型>{}

确认时机
    
  	//创建类的对象时

确认格式:

  基础JDK6.0(包含)以前,且实现类存在泛型
      
	父接口名<数据类型> 实现类对象名 = new 实现类类名<数据类型>(实参);

  基础JDK7.0(包含)以后,且实现类存在泛型,新增格式:

	 父接口名<数据类型> 实现类对象名 = new 实现类类名<>(实参);

  实现类不存在泛型:

	  父接口名 实现类对象名 = new 实现类类名(实参);

注意事项:

1.确认泛型时,前后<>中的数据类型必须一致,否则编译报错

2.如果含有泛型的接口含有没有泛型的实现类时,不推荐使用多态形式创建该实现类对象
2、含有泛型的类
声明格式:

	public class 类名<泛型> {}

确认时机:

	创建类的对象时

确认格式:

	基础JDK6.0(包含)以前,且实现类存在泛型

		类名<数据类型> 对象名 = new 类名<数据类型>(实参);

	基础JDK7.0(包含)以后,且实现类存在泛型,新增格式:

		类名<数据类型> 对象名 = new 类名<>(实参);

注意事项:

	1.确认泛型时,前后<>中的数据类型必须一致,否则编译报错
3、含有泛型的方法
声明格式:

	修饰符 <泛型> 返回类型 方法名 () {}

确认时机:

	调用该方法时

确认格式:

	方法名(实参);


2.泛型的高级应用:通配符
通配符:

	针对含有泛型方法特殊形式的简化

前提:

	该泛型方法的形参列表是一个含有该泛型类或者接口

格式:

	优化前:

		修饰符 <泛型> 返回类型 方法名 (类名或接口名<泛型> 对象名) {}

优化后:

    修饰符 返回类型 方法名 (类名或接口名<?> 对象名) {}

确认时间:

	调用方法传参时
-------------------------------------------------------------------------------------

通配符的上限和下限

	通配符上限:

		格式:<? extends 数据类型>

	含义:传递的类型必须是该类型本身或者是该类型的子类类型

	通配符下限:

		格式:<? super 数据类型>

	含义:传递的类型必须是该类型本身或者是该类型的父类类型

5、List接口

1.接口的特点

​ 1.List集合都是有序的集合

​ 注意:集合的有序性指代存储元素和获取元素的顺序,不是排序的含义

​ 有序集合:存储元素的顺序和获取元素的顺序是一致的

​ 无序集合:存储元素的顺序和获取元素的数据不是一致的

​ 2.List集合都是含有索引的集合

​ 集合索引:元素在集合中的位置,从0开始,依次递增,最大的索引长度-1

​ 3.List集合都可以存储重复元素

​ 4.List集合中索引的实现方式有4种(涉及数据结构)

数组结构

​ 含义:

​ 其实就是前面学习的对象数组

​ 特点:

​ 相比链表结构:查询效率高,增删效率低

链表结构

​ 其实就是程序中的一个特殊类

​ 分类:

​ 单向链表

​ 链表对象至少含有两个属性:

​ 当前元素(数据类型:泛型)

​ 下一个链表对象(数据类型:链表类型)

​ 双向链表

​ 链表对象至少含有三个属性:

​ 当前元素(数据类型:泛型)

​ 下一个链表对象(数据类型:链表类型)

​ 上一个链表对象(数据类型:链表类型)

​ 特点:

​ 相比数组数据结构:增删效率高,查询效率低

​ 注意:

​ 链表结构长度越长效率越低

队列结构

栈结构

​ 5.List集合都含有特殊的迭代器

2.接口的方法

void add(int index,E element)

在列表的指定位置插入指定元素(可选操作)。

E get(int index)

返回列表中指定位置的元素。

int indexOf(Object o)

返回此列表中第一次出现的指定元素的索引;如果列表不包含此元素,则返回 -1。

int lastIndexOf(Object o)

返回此列表中最后出现的指定元素的索引;如果列表不包含此元素,则返回 -1。

ListIterator listIterator()

返回此列表元素的列表迭代器(按适当顺序)。

E remove(int index)

移除列表中指定位置的元素(可选操作)。

E set(int index,E element)

用指定元素替换列表中指定位置的元素(可选操作)。

List subList(int fromIndex,int toIndex)

返回列表中指定的 fromIndex(包括)和 toIndex(不包括)之间的部分视图

3.List集合的遍历方式:6种
		1.迭代器

		2.增强for

		3.Stream流的forEach()

		4.Iterable接口的forEach()

		5.List特有迭代器
		
		6.普通for循环

6、ArrayList类

1.类的特点

​ 1.ArrayList集合底层数据结构是数组结构

​ 2.ArrayList集合可以存储null元素,在使用ArrayList集合元素之前,先针对元素进行非空校验,防止空指针异常

​ 3.ArrayList集合是线程不安全的集合,只适用于单线程程序,如果在多线程中进行使用,需要手动添加线程安全

​ 4.ArrayList集合是有序的集合

​ 有序集合:存储元素的顺序和获取元素的顺序是一致的

​ 5.ArrayList集合是含有索引集合

​ 6.ArrayList集合可以存储重复元素

​ 7.ArrayList集合适用于List集合的6种遍历方式

​ 类的位置

​ java.util

​ 类的构造器

​ public ArrayList()

​ 构造一个初始容量为 10 的空列表。

​ 类的方法

​ 详见Collection接口和List接口的常用方法

​ 注意事项:

​ 针对多个元素进行查询操作较多的时候

2.ArrayList集合的源码分析(基于JDK8.0):
1.ArrayList集合底层数组的初始容量

​ 初始容量是多少取决于创建对象时所选择的构造器

​ (1)ArrayList()

​ 初始容量:0

​ (2)ArrayList(Collection c)

​ 初始容量:参数集合的长度

​ (3)ArrayList(int initialCapacity)

​ 初始容量:自定义

2.无参构造器的扩容原理:

​ 第一次添加元素时:

​ 扩容原理:10

​ 后续添加元素时:

​ 扩容原理:原来数组长度 + (原来数组长度 >> 1);

3.JDK6.0(包含)以前和JDK7.0(包含)以后的区别:

​ (1)无参构造器初始容量不同:

​ JDK6.0(包含)以前:

​ 初始容量:10

​ JDK7.0(包含)以后:

​ 初始容量:0

​ (2)无参构造器的扩容原理不同:

​ JDK6.0(包含)以前:

​ 扩容原理:(原来数组长度 * 3)/2 + 1;

​ JDK7.0(包含)以后:

​ 扩容原理:

​ 第一次添加元素时:

​ 扩容原理:10

​ 后续添加元素时:

​ 原来数组长度 + (原来数组长度 >> 1);

day18(6.21)

1、Vector类

除了list集合的特点外,vector类的特点:

1.Vector集合底层数据结构是数组结构

​ 2.Vector集合可以存储null元素,在使用Vector集合元素之前,先针对元素进行非空校验,防止空指针异常

​ 3.Vector集合是线程安全的集合,只适用于多线程程序,如果在单线程中进行使用,执行效率低

类的构造器

​ public Vector()

​ 构造一个空向量,使其内部数据数组的大小为 10,其标准容量增量为零。

​ ArrayList集合和Vector集合的区别:

​ 1、线程安全性:

​ ArrayList集合:ArrayList集合是线程不安全的集合,只适用于单线程程序,如果在多线程中进行使用,需要手动添加线程安全

​ Vector集合:Vector集合是线程安全的集合,只适用于多线程程序,如果在单线程中进行使用,执行效率低

​ 2.无参构造器底层的初始容量

​ ArrayList集合:

​ JDK6.0(包含)以前:

​ 初始容量:10

​ JDK7.0(包含)以后:

​ 初始容量:0

​ Vector集合:

​ 初始容量:10

​ 3.底层数组影响扩容的因素不同

​ ArrayList集合:是否为第一次扩容

​ Vector集合:增量参数是否大于0

​ 4.底层数组的扩容规则不同

​ ArrayList集合:

​ JDK6.0(包含)以前:

​ 扩容原理:(原来数组长度 * 3)/2 + 1;

​ JDK7.0(包含)以后:

​ 扩容原理:

​ 第一次添加元素时:

​ 扩容原理:10

​ 后续添加元素时:

​ 原来数组长度 + (原来数组长度 >> 1);

​ Vector集合:

​ 当增量参数 > 0:

​ 扩容原理:

​ 原来数组长度 + 增量参数;

​ 当增量参数 <= 0;

​ 扩容原理:

​ 原来数组长度 + 原来数组长度;

2.LinkedList类

类的特点

​ 1.LinkedList集合底层的数据结构为"链接列表结构",并且是一个"双向链接列表结构"

​ 2.LinkedList集合可以存储null元素,在使用集合中元素之前需要进行非空校验,防止空指针异常

​ 3.LinkedList集合不是线程安全的,适用于单线程程序,如果在多线程中进行使用,需要手动添加线程安全

​ 4.LinkedList集合是有序的集合

​ 有序集合:存储元素的顺序和获取元素的顺序是一致的

​ 5.LinkedList集合是含有索引集合

​ 6.LinkedList集合可以存储重复元素

​ 7.LinkedList集合适用于List集合的6种遍历方式

类的构造器

​ public LinkedList()

​ 构造一个空列表。

类的方法

public void addFirst(E e)

将指定元素插入此列表的开头。

public void addLast(E e)

将指定元素添加到此列表的结尾。

public E getFirst()

返回此列表的第一个元素。

public E getLast()

返回此列表的最后一个元素。

public E removeFirst()

移除并返回此列表的第一个元素。

public E removeLast()

移除并返回此列表的最后一个元素。

3、set接口

特点:

​ 1、不可存放重复元素

​ 2、即使有序的也是无序的

​ 3、没有索引

​ 4、遍历方式只有Collection集合的那四种

1、HashSet类

特点:

​ 1.HashSet集合底层封装一个HashMap实例

​ HashMap集合底层的数据结构:哈希表

​ JDK7.0(包含)以前:

​ 存储链接列表对象的数组

​ JDK8.0(包含)以后

​ 存储链接列表对象或红黑树对象的数组

​ 2.HashSet集合是无序的集合

​ 3.HashSet集合无法保证元素在集合中的顺序永远不会改变

原因:元素的存储顺序和底层数组的长度有关,一旦数组发生扩容或缩减元素的位置有可能发生变化

​ 4.HashSet集合可以存储null元素,使用集合元素时,需要进行非空校验,防止空指针异常

​ 5.HashSet集合是线程不安全的,适用于单线程程序,如果在多线程中进行使用,需要手动添加线程安全

类的构造器

​ public HashSet()

​ 构造一个新的空 set,其底层 HashMap 实例的默认初始容量是 16,加载因子是 0.75。


对于hashcode,它的作用只是用来计算当前元素要存的索引值,由于初始长度为16,在添加元素时,会根据添加元素生成对应的值,除以数组长度16,得到对应的所在数组索引(0~15)。
添加元素时,元素对应的值与16取余,得到的数存放在数组中,这就是为什么hashset数组无序的原因。
而对于equals方法,如果算出的索引上有元素,则用equals进行内容的判断,若为true,则为同一个元素,将其覆盖。若为FALSE,则将其所在索引处使用链表结构,将其“挂”在该索引。
但是当所在索引全部存满,就导致查询的效率很低,为了提高效率才进行了扩容(当前数组长度x0.75)
数组被扩容,元素索引位置要重新计算。

再次存满再次扩容,每次扩容都会导致数组的再次运算,数量巨大,故在数组长度大于64后,就不在进行扩容,使用红黑树结构进行
HashSet相关面试题
1、HashSet集合如何过滤重复元素

​ 1.HashSet集合过滤重复元素和地址值无关

​ 2.HashSet集合过滤重复元素和hashCode()有关,重写Object类的hashCode()

​ 3.HashSet集合过滤重复元素和equals()有关,重写Object类的equals()

 			HashSet<String> set = new HashSet<>();
    		 	 String s1 = new String("abc");
     			 String s2 = new String("abc");
      			 System.out.println(s1 == s2);//false
				set.add(s1);
				set.add(s2);
结果:只会存储一个"abc"
    
--------------------------------------
    原因:
    先将“abc”转成Hash值,如果两个Hash值相同,就会调用equals方法判断两个内容是否一致,都为TRUE则认定为同一个对象,就只会存储一个
    如果hash值不同,则不会启用equals方法。或hash值相同但equals为FALSE则为两个对象,不会过滤
    
2、重写hashCode()时重要参数为什么是31?
	1.为了减少hashCode值的重复概率,这个数必须是质数

​ 2.这个数不宜过小(原因:过小增大重复概率)

​ 3.这个数不宜过大(原因:过大会有可能超出int范围,造成数据的精度的损失,增大重复概率)

​ 4.通过"泊松分布"计算出数字29和31是较为合适的数字

​ 5.通过数学科学计数法的改写,29可以写成25-3,31可以写成25-1,31的格式和整数取值范围相似或者二进制满位原因,最终选择31

2、LinkedHashSet类

​ 1.LinkedHashSet集合是有序的集合

​ 2.LinkedHashSet集合底层的数据结构是"哈希表 + 链表"

​ 链表:维护着LinkedHashSet集合有序

​ 3.LinkedHashSet集合可以存储null元素,使用集合元素时,需要进行非空校验,防止空指针异常

​ 4.LinkedHashSet集合是线程不安全的,适用于单线程程序,如果在多线程中进行使用,需要手动添加线程安全

类的构造器

​ public LinkedHashSet()

​ 构造一个带默认初始容量 (16) 和加载因子 (0.75) 的新空链接哈希 set。

3、红黑树数据结构:

含义:

带有红结点和黑结点的二叉树结构,其实也是程序中比较特殊的类

特点:

1.查询效率高,增删效率高

2.结点对象可以根据int数据进行大小排序

设计:

当前元素(泛型)

父结点对象(结点对象类型)

子左结点对象(结点对象类型)

子右结点对象(结点对象类型)

颜色属性(boolean)

注意:

红黑树数据结构适用于数据量较多时候,不适合少量数据

红黑树数据结构中的每个结点比较数据必须落实在"int类型"

4、TreeSet类

特点:

​ 1、TreeSet集合底层数据结构是红黑树

​ 2、使用元素的自然顺序对元素进行排序,或者根据创建set时提供的Comparator进行排序

​ 3、TreeSet集合是无序的集合,存储和取出顺序不一致

​ 4、TreeSet集合不可以存储null

​ 5、TreeSet集合是线程不安全的集合,适用于单线程,如果在多线程中使用,需手动添加线程安全

类的构造器

​ public TreeSet()

​ 构造一个新的空 set,该 set 根据其元素的自然顺序进行排序。

​ public TreeSet(Comparator comparator)

​ 构造一个新的空 TreeSet,它根据指定比较器进行排序。

注意事项:

​ 针对TreeSet中元素类型必须定义比较器(手动编写比较规则)

自然顺序比较器

​ 实现Comparable接口,重写compareTo(T o)

定制顺序比较器

​ 实现Comparator接口,重写int compare(T o1,T o2)

1.自然顺序比较器

实现Comparable接口,重写compareTo(T o)

步骤:

1.TreeSet集合元素的类型实现Comparable接口

2.TreeSet集合元素的类型中重写Comparable接口的compareTo(T o),指定比较规则

public class Student implements Comparable<Student> {

@Override
    public int compareTo(Student s) {
        return this.id - s.id;
    }
    
   }

2.定制顺序比较器

//创建TreeSet集合对象
        TreeSet<Student> set = new TreeSet<>(new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                int result = s1.getAge() - s2.getAge();

                if (result == 0) {
                    //启动第二级比较规则
                    result = s1.getId() - s2.getId();
                }

                return result;
            }
        });

Collectons类的方法

public static boolean addAll(Collection c,T… elements)

将所有指定元素添加到指定 collection 中。

public static void reverse(List list)

反转指定列表中元素的顺序。

public static void shuffle(List list)

使用默认随机源对指定列表进行置换。

public static > void sort(List list)

根据元素的自然顺序 对指定列表按升序进行排序。

public static void sort(List list,Comparator c)

根据指定比较器产生的顺序对指定列表进行排序

day19(6.22)-day20(6.23)

1、Map(接口)集合

接口的特点

​ 1.双列集合的顶级接口

​ 2.Map集合以"对"为单位进行存储,其中一个key,一个value

​ 3.key和value之间的关系是"映射关系"

​ 4.key和value的泛型类型可以相同,也可以不同

​ 5.在映射关系中,key不可以重复,value可以重复

接口的方法

V put(K key,V value)

将指定的值与此映射中的指定键关联(可选操作)。

void clear()

从此映射中移除所有映射关系(可选操作)。

boolean containsKey(Object key)

如果此映射包含指定键的映射关系,则返回 true。

boolean containsValue(Object value)

如果此映射将一个或多个键映射到指定值,则返回 true。

V get(Object key)

返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null。

boolean isEmpty()

如果此映射未包含键-值映射关系,则返回 true。

V remove(Object key)

如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。

int size()

返回此映射中的键-值映射关系数

Set keySet()

返回此映射中包含的键的 Set 视图。

Collection values()

返回此映射中包含的值的 Collection 视图。

Set> entrySet()

返回此映射中包含的映射关系的 Set 视图。

default void forEach(BiConsumer action)

针对map进行遍历


Map集合的遍历方式

	//使用多态的形式创建Map集合
        Map<String,String> map = new LinkedHashMap<>();

        //添加元素
        map.put("探险家","伊泽瑞尔");

	 //获取Map集合的键集
        Set<String> keys = map.keySet();
	 -------------------------------------------------------
1、遍历方式:键找值	
   
        //遍历键集
        for (String key : keys) {
            String value = map.get(key);
            System.out.println(key + "=" + value);
        }
	-------------------------------------------------------
2、遍历方式:键值对 对象
   Map.Entry<K,V>接口

		以key和value封装对象的集合

	接口的位置

		Map接口中的Entry<K,V>

	接口的方法

		K getKey()

			返回与此项对应的键。

		V getValue()

			返回与此项对应的值。
    -------------------------------------------------------
          //获取Map集合中所有键值对对象的集合
        Set<Map.Entry<String, String>> entries = map.entrySet();

        for (Map.Entry<String, String> entry : entries) {
            String key = entry.getKey();
            String value = entry.getValue();

            System.out.println(key + "=" + value);
        }
	-------------------------------------------------------
            
3、forEach
            
 		 //遍历Map集合
        map.forEach(new BiConsumer<String, String>() {
            @Override
            public void accept(String key, String value) {
                System.out.println(key + "=" + value);
            }
        });

1.HashMap类

类的特点

​ 1.HashMap集合的底层数据结构是"哈希表"

​ JDK7.0(包含)以前:存储链表对象的数组

​ JDK8.0(包含)以后:存储链表对象或者红黑树对象的数组

​ 2.HashMap集合可以存储null值和null键,使用键或值的时候需要进行非空校验,防止空指针异常

​ 3.HashMap集合是无序的集合

​ 4.HashMap集合无法保证元素在集合中的位置永远不会改变

​ 5.HashMap集合是线程不安全的,只适用于单线程程序,如果在多线程中进行使用,需要手动添加线程安全

类的构造器

​ public HashMap()

​ 构造一个具有默认初始容量 (16) 和默认加载因子 (0.75) 的空 HashMap。

类的方法

​ 详见Map接口的常用方法

2、HashMap集合元素存储过程的源码分析(基于JDK8.0):

1.相关名词解释

​ 桶:底层数组的实时长度

​ 桶元素:底层数组中存储的元素(null,链表对象,红黑树)

​ 初始容量:底层数组的初始长度

​ 加载因子:底层数组的进行扩容前阈值的参数

​ 阈值:底层数组扩容前的标准

​ 阈值 = (int)(底层数组长度 * 加载因子)

2.涉及成员变量,成员常量和方法的局部变量的备忘录

​ 成员变量或成员常量:

​ DEFAULT_INITIAL_CAPACITY(int):底层数组默认的初始长度

​ MAXIMUM_CAPACITY(int):底层数组最大长度

​ DEFAULT_LOAD_FACTOR(float):默认的加载因子

​ TREEIFY_THRESHOLD(int):树化标准之一,链表对象的判断条件

​ UNTREEIFY_THRESHOLD(int):链表化标准,红黑树结构中结点对象的数量

​ MIN_TREEIFY_CAPACITY(int):树化标准之一,底层数组进行树化最小长度

​ table(Node[]):底层数组

​ entrySet(Set>):底层键值对对象的集合

​ size(int):集合中元素的数量

​ modCount(int):集合中元素的计数器变量

​ threshold(int):扩容前的阈值变量

​ loadFactor(float):加载因子变量

​ putVal()的局部变量:

​ hash(int):通过key的hashCode重新计算的hash值

​ key(K):待添加元素的键

​ value(V):待添加元素的值

​ tab(Node[]):待存储操作的底层数组

​ p(Node):待存储元素在底层数组中待存储的索引位置上的桶元素

​ n(int):待存储操作底层数组的长度

​ i(int):待存储元素在底层数组中待存储的索引位置

​ e(Node):当前红黑树对象或链表对象的下一位对象

​ k(K):待存储元素在底层数组中待存储的索引位置上的桶元素的key

resize()的局部变量:

​ oldTab(Node[]):待重置前操作的底层数组

​ oldCap(int):待重置前操作底层数组的长度

​ oldThr(int):待重置前操作底层数组的阈值

​ newCap(int):待重置后操作底层数组的长度

​ newThr(int):待重置后操作底层数组的阈值

​ hash(Object key)的局部变量

​ h(int):key的hashCode值

treeifyBin()的局部变量:

​ n(int):底层待操作数组的长度

​ index(int):

​ e(Node):

3.底层数组的初始容量和初始加载因子

​ 初始容量和初始加载因子是多少取决于创建对象时的构造器

(1)HashMap()

​ 初始容量:创建对象时没有进行初始化操作,会在第一次添加元素时进行初始化操作,初始容量为16

​ 初始加载因子:0.75

(2)HashMap(int initialCapacity)

​ 初始容量:自定义

​ 初始加载因子:0.75

(3)HashMap(int initialCapacity, float loadFactor)

​ 初始容量:自定义

​ 初始加载因子:自定义

(4)HashMap(Map m)

​ 初始容量:参数集合的长度

​ 加载因子:参数集合长度的加载因子

4.第一次添加元素时的扩容规则

​ 扩容规则:16

5.如何确认元素在底层数组中的索引位置

(1)将key的hashCode值重新计算hash值【怎么算的?】

​ 代码:(key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E1y9PcXM-1658415050659)(/D:/Typora/bin/document/img/1655895986423.png)]

​ 如图为key的hashcode对应值1183939,由于为正整数,所以他的源码,反码,补码都一样,将hashcode的值右移16,再将hashcode值与右移16位进行异或运算(当两位相同时返回0,不同时返回1)

解释:hash值的补码32位中,高16位的补码和hashCode值的高16位补码相同,低16位是hashCode值的高16位补码和hashCode的低16位补码进行按位异或

​ 目的:重新计算hashCode是为了将元素可以更加均匀分布在各个桶之间

(2)根据hash值和底层数组长度计算存储的索引位置

​ 代码:(n - 1) & hash

​ 解释:将hash值和底层数组长度-1进行按位与,得到结果是该元素在底层数组中存储的索引位置

6.后续添加元素时的扩容规则

​ 扩容规则:

​ 原来底层数组长度 << 1

7.底层数组中链表对象何时进行红黑树对象的转换(树化)

​ (1)链表对象长度达到8时

​ (2)底层数组长度达到64

8.底层数组中红黑树对象何时进行链表对象的转换(链表化)

​ (1)红黑树结构中结点对象的数量降至6个

9.JDK7.0和JDK8.0的区别

(1)底层数组的桶元素内容不同

​ JDK7.0(包含)以前:null,链表对象

​ JDK8.0(包含)以后:null,链表对象,红黑树对象

(2)底层数组的数据类型不同

​ JDK7.0(包含)以前:Entry[]

​ JDK8.0(包含)以后:Node[]

(3)无参构造器底层数组的初始容量不同:

​ JDK7.0(包含)以前:16

​ JDK8.0(包含)以后:创建对象时没有进行初始化操作,会在第一次添加元素时进行初始化操作,初始容量为16

(4)hash算法不同:

​ JDK7.0(包含)以前:

​ h ^= k.hashCode();

​ h ^= (h >>> 20) ^ (h >>> 12);

​ h ^ (h >>> 7) ^ (h >>> 4);

JDK8.0(包含)以后:

​ (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16)

(5)底层数组的扩容规则:

​ JDK7.0(包含)以前:

​ 2 * 原来底层数组长度

​ JDK8.0(包含)以后:

​ 原来底层数组长度 << 1

4.默认的加载因子为什么是0.75?

​ 默认加载因子 (.75) 在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查询(时间)成本;加载因子过低虽然减少了查询(时间)开销,但同时也增加了空间成本;

2.LinkedHashMap类

类的特点

​ 1.LinkedHashMap集合的底层数据结构是"哈希表+链表"

​ 链表的作用:维护元素的有序性

​ 2.LinkedHashMap集合是有序的集合

​ 3.LinkedHashMap集合可以存储null值和null键,使用键或值的时候需要进行非空校验,防止空指针异常

​ 4.LinkedHashMap集合是线程不安全的,只适用于单线程程序,如果在多线程中进行使用,需要手动添加线程安全

类的构造器

​ public LinkedHashMap()

​ 构造一个带默认初始容量 (16) 和加载因子 (0.75) 的空插入顺序 LinkedHashMap 实例。

类的方法

​ 详见Map接口的常用方法

3.TreeMap类

类的特点

​ 1.TreeMap集合底层的数据结构是"红黑树结构"

​ 2.TreeMap集合可以根据映射关系中键的自然顺序进行排序,或者根据创建映射时提供的 Comparator进行排序,具体取决于使用的构造方法。

​ 3.TreeMap集合无序的集合

​ 4.TreeMap集合不可以存储null键,存储时需要针对每个键进行非空校验,防止空指针异常,并且TreeMap集合可以存储null值,但获取

时需要针对每个值进行非空校验,防止空指针异常

​ 5.TreeMap集合是线程不安全的,适用于单线程程序,如果在多线程中进行使用需要手动添加线程安全

类的构造器

​ public TreeMap()

​ 使用键的自然顺序构造一个新的、空的树映射。

​ public TreeMap(Comparator comparator)

​ 构造一个新的、空的树映射,该映射根据给定比较器进行排序

类的方法

详见Map集合的常用方法

4.Hashtable类

类的特点

​ 1.Hashtable集合底层的数据结构是"哈希表"

​ 哈希表:存储链表对象的数组

​ 2.Hashtable集合不可以存储null键和null值,需要在存储元素时进行非空校验,防止空指针异常

​ 3.Hashtable集合是无序集合

​ 4.Hashtable集合无法保证元素的顺序永远不会改变

​ 5.Hashtable集合是线程安全的,适用于多线程程序,如果在单线程中进行使用,执行效率过低

类的构造器

​ public Hashtable()

​ 用默认的初始容量 (11) 和加载因子 (0.75) 构造一个新的空哈希表。

类的方法

常见Map集合的常见方法

5.HashMap和Hashtable之间的区别:

​ 1.null键和null值是否可以存储不同

​ HashMap:可以

​ Hashtable:不可以

​ 2.线程安全性不同

​ HashMap:线程不安全

​ Hashtable:线程安全

​ 3.使用无参构造器创建对象时底层数组的初始容量不同

​ HashMap:

​ JDK7.0(包含)以前:16

​ JDK8.0(包含)以后:先不进行初始化,在第一次添加元素时初始化16

​ Hashtable:11

​ 4.底层数组存储桶元素不同

​ HashMap:

​ JDK7.0(包含)以前:null,链表对象

​ JDK8.0(包含)以后:null,链表对象,红黑树对象

​ Hashtable:

​ null,链表对象

​ 5.底层数组的扩容规则不同:

​ HashMap:

​ JDK7.0(包含)以前:

​ 2 * 原来底层数组长度

​ JDK8.0(包含)以后:

​ 原来底层数组长度 << 1

​ Hashtable:

​ JDK6.0(包含)以前:

​ 原来底层数组长度 * 2 + 1;

​ JDK7.0(包含)以后:

​ (原来底层数组长度 << 1) + 1

6.Properties类

类的特点

​ Properties 类表示了一个持久(存储在硬盘中)的属性集(配置文件)。

类的构造器

​ public Properties()

​ 创建一个无默认值的空属性列表。

类的方法

public String getProperty(String key)

用指定的键在此属性列表中搜索属性。

public void load(InputStream inStream)

从输入流中读取属性列表(键和元素对)。

public void load(Reader reader)

按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。

public Set stringPropertyNames()

返回此属性列表中的键集,其中该键及其对应值是字符串,如果在主属性列表中未找到同名的键,则还包括默认属性列表中不同的键

 //创建属性集对象
        Properties pro = new Properties();

        //通过IO流读取配置文件中的内容(键值对)
        pro.load(new FileInputStream("Properties.properties"));

        //遍历属性集对象
        Set<String> keys = pro.stringPropertyNames();

        //遍历键集
        for (String key : keys) {
            String value = pro.getProperty(key);
            System.out.println(key + "=" + value);
        }

2、多线程

并发和并行:

​ 并发:在同一时间段内发生的多件事情

​ 并行:在同一时间发生的多件事情
进程和线程:

​ CPU核心执行规则:每个核心在所有进程中进行高速无规则的切换动作

​ 进程:操作系统中的应用程序,每个应用程序至少包含一条进程,程序进程由CPU核心进行控制

​ 线程:每个进程中的线程,每条进程至少包含一条线程,程序线程由CPU核心中的线程进行控制

1、Thread类(java.lang)

类的特点

​ 线程是程序中的执行线程

类的构造器

public Thread()

分配新的 Thread 对象。

public Thread(String name)

分配新的 Thread 对象。

public Thread(Runnable target)

分配新的 Thread 对象

public Thread(Runnable target,String name)

分配新的 Thread 对象

类的方法

public static Thread currentThread()

返回对当前正在执行的线程对象的引用。

public final String getName()

返回该线程的名称。

public static void sleep(long millis)

在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。

public void run()

如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;

否则,该方法不执行任何操作并返回。

public void start()

使该线程开始执行;Java 虚拟机调用该线程的 run 方法。

1.线程开启的方式:(4种)

​ 1.继承Thread类

​ 2.实现Runnable接口

​ 3.实现Callable接口(暂不涉及)

​ 4.线程池(暂不涉及)

1.继承Thread类

​ 1)创建Thread类的子类

​ 2)根据父类生成合适的构造器

​ 3)重写Thread的run()方法

​ 4)在测试类中需要几个执行线程就创建几个Thread类的子类对象

​ 5)开启线程

弊端:由于类是单继承,所以有局限性

2.实现Runnable接口

​ 1)创建实现Runnable接口的实现类

​ 2)重写Runnable接口的抽象方法run()

​ 3)创建Runnable实现类对象

​ 4)需要几条线程就将Runnable接口实现类对象作为参数创建几个线程对象

​ 5)开启线程

 	 	//3.创建Runnable接口的实现类对象
        SubRunnable sr = new SubRunnable();

        //4.需要多少条线程,就将Runnable接口实现类对象作为参数创建多少个线程对象
        Thread t1 = new Thread(sr, "自定义执行线程对象1");

        //5.开启线程
        t1.start();
--------------------------------------------------------------------------------------
   	//1.创建Runnable接口实现类
    public class SubRunnable implements Runnable {
    //2.重写接口中的抽象方法run()
    @Override
    public void run() {
        //获取当前执行线程对象的名字
        String name = Thread.currentThread().getName();

        for (int i = 1; i <= 30; i++) {
            System.out.println(name + ":" + i);
        }
    }

或通过匿名内部类实现

public static void main(String[] args) {
        //使用匿名内部类创建Runnable接口的实现类对象
        Runnable run = new Runnable() {
            @Override
            public void run() {
                //获取当前执行线程对象的名字
                String name = Thread.currentThread().getName();

                for (int i = 1; i <= 30; i++) {
                    System.out.println(name + ":" + i);
                }
            }
        };

        //需要多少条线程,就将Runnable接口实现类对象作为参数创建多少个线程对象
        Thread t1 = new Thread(run, "自定义执行线程对象1");

        //开启线程
        t1.start();

2.线程安全问题(线程同步问题)

​ 1.同一数据被获取或操作多次

​ 2.出现了非法数据

线程安全问题的解决方法:

​ 1.同步代码块(同步方法只是特殊形式的解决方案或者同步代码块的简化方式)

​ 2.Lock锁

synchronized关键字

​ 含义:同步

​ 用来修饰同步代码块和同步方法

​ 特点:被synchronized关键字修饰,同一时间只能被一条线程所执行,其他线程如果想要执行,需在同步代码块或同步方法中进行“阻塞”(如使用sleep使其休眠一会)

3.同步代码块和同步方法

1.同步代码块

​ 被synchronized修饰的代码

synchronized (同步对象) {

	//可能出现线程安全的代码

}

特点:

	同步代码块同一时间只能被一条线程所执行,其它线程在同步代码块外进行"阻塞",同步代码中的线程执行完毕后,再进行资源抢夺,如果抢夺到资源,执行同步代码块,如果没有抢夺到资源,继续"阻塞"

注意事项:

1.多条线程执行同一个同步代码块,同步代码块中的同步对象必须是相同的,如果不同,无法保证线程安全

2.多条线程处理同一数据资源对象如果多条线程执行动作相同,同步对象就是多条线程同一动作对象

如果多条线程执行动作不同,同步对象就是多条线程处理的同一数据资源对象

如果多条线程执行动作是相同的静态方法,同步对象就是多条线程同一动作所属类的字节码文件对象

2.同步方法

​ 被synchronized修饰的方法

​ 特点:同步方法在同一时间只能被一条线程所调用,该同步方法没有执行完毕前,其它线程无法进行调用,需要在外进行"阻塞"

​ 格式:

修饰符 synchronized 返回类型 方法名 () {
		
    //可能出现线程安全的代码

}

注意:

1.当多条线程执行的动作相同时,可以使用同步方法进行同步代码块的简化;当多条线程执行的动作不同时,现阶段必须使用同步代码块解决线程安全问题

2.同步方法的权限访问级别推荐使用private和public

同步代码块和同步锁的区别:

同步锁用于

4.线程间通信(等待唤醒机制)

含义:

让多条线程间通过"等待"和"唤醒"产生通信,从而让线程可以按照一定的规则进行执行

方法:

​ public final void wait()

​ 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。

​ public final void notify()

​ 唤醒在此对象监视器上等待的单个线程。

​ public final void notifyAll()

​ 唤醒在此对象监视器上等待的所有线程。

分类:

​ 生产者线程:负责生产同一资源对象的线程

​ 消费者线程:负责消费同一资源对象的线程

单例设计模式线程问题:

​ 在单线程中,使用单例设计模式的懒汉模式没有问题。但在多线程中会产生运行出的地址值不一致的问题

public class CEO implements Runnable{
    //声明私有唯一静态对象变量
    private static CEO ceo;

//给外界提供唯一公共访问
    private CEO getCEO() {
        return ceo;
    }
//构造器私有化
    private CEO() {
    }
//在第一次被调用时才会创建对象
    public static CEO method(){
        if(ceo==null){
            ceo=new CEO();
        }
        return ceo;
    }

//重写Runnable的run方法
    @Override
    public void run() {
        System.out.println(getCEO());
    }
}
----------------------------------------------
    public static void main(String[] args) {
        CEO method = CEO.method();
        Thread t1=new Thread(method);
        Thread t2=new Thread(method);
        t1.start();
        t2.start();
    }
通过Runnable可解决该问题
最优解决:
package com.atguigu.thread.demo12;

/**
 * @ClassName CEO
 * @Description 此类用于演示功能
 * @Author Shark
 * @DateTime 2022年06月23日 14时36分59秒
 * @Version 1.0
 */
public class CEO {
    private static CEO ceo;

    private CEO() {
    }

    public static CEO getCEO() {

        if (ceo == null) {
            method();
        }

        return ceo;
    }

    private static synchronized void method () {
        if (ceo == null) {
            ceo = new CEO();
        }
    }
}
-----------------------------------------------------------------------
      public static void main(String[] args) {
        Runnable run = new Runnable() {
            @Override
            public void run() {
                System.out.println(CEO.getCEO());
            }
        };

        Thread t1 = new Thread(run);
        Thread t2 = new Thread(run);
        Thread t3 = new Thread(run);

        t1.start();
        t2.start();
        t3.start();
    }

4、多线程面试题

1.Java中线程的状态分为几种?分别是?

​ NEW(新建)

​ 含义:线程对象被创建,但没有执行start()的状态

​ RUNNABLE(运行)

​ 含义:线程正常运行的状态

​ BLOCKED(阻塞)

​ 含义:线程遇到线程安全处理,但线程对象并没有抢夺到同步对象或锁对象资源的状态

​ WAITING(无限等待)

​ 含义:线程对象被执行wait()状态

​ TIMED_WAITING(计时等待)

​ 含义:线程对象被执行sleep()状态

​ TERMINATED(终止)

​ 含义:线程对象执行完毕,在内存中被垃圾回收器回收的状态

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y9YdrjEf-1658415050660)(/D:/Typora/bin/document/img/1656035604406.png)]

2.Java中线程如果进行状态的转化?

新建状态的转换

​ 转换关系:

​ 可以转换成"运行"状态:

​ 被执行start()时

运行状态的转换

​ 转换关系:

​ 可以转换成"阻塞"状态:

​ 线程对象遇到线程安全处理,但线程对象并没有抢夺到同步对象或锁对象资源的状态

​ 可以转换成"无限等待"状态:

​ 线程对象被执行wait()状态

​ 可以转换成"计时等待"状态:

​ 线程对象被执行sleep(时间参数)状态

​ 线程对象被执行wait(时间参数)状态

​ 可以转换成"终止"状态:

​ 线程对象执行完毕,在内存中被垃圾回收器回收的状态

阻塞状态的转换

​ 转换关系:

​ 可以转换成"运行"状态:

​ 进行线程安全处理的线程对象执行完毕,但当前线程对象抢夺到同步对象或锁对象资源的状态

无限等待状态的转换

​ 转换关系:

​ 当有线程安全处理时:

​ 可以转换成"运行"状态:

​ 进行线程安全处理的线程对象执行完毕并且当前线程对象被执行notify()或notifyAll(),当前线程对象抢夺 到同步对象或锁对象资源的状态

​ 可以转换成"阻塞"状态:

​ 进行线程安全处理的线程对象执行完毕并且当前线程对象被执行notify()或notifyAll(),当前线程对象

​ 没有抢夺到同步对象或锁对象资源的状态

​ 当没有线程安全处理时:

​ 可以转换成"运行"状态:

​ 当前线程对象被执行notify()或notifyAll()

计时等待状态的转换

​ 转换关系:

​ 当有线程安全处理时:

​ 可以转换成"运行"状态:

​ 规则时间到(wait(时间参数)在时间内被执行notify()或notifyAll()),当前线程对象抢夺到同步对象或

​ 锁对象资源的状态

​ 可以转换成"阻塞"状态:

​ 规则时间到(wait(时间参数)在时间内被执行notify()或notifyAll()),当前线程对象没有抢夺到同步对

​ 象或锁对象资源的状态

​ 当没有线程安全处理时:

​ 可以转换成"运行"状态:

​ 规则时间到(wait(时间参数)在时间内被执行notify()或notifyAll())

day21.网络编程(6.25)

今日重点:
  1.会使用Lombok快速创建一个javabean
  2.会使用TCP协议完成一次客户端和服务端的交互
  3.知道客户端和服务器交互过程
  4.知道三次握手

1、Commons-io工具包(IO流扩展)

目的:大大的简化对于io流的拷贝目录的递归调用

但是其实底层还是封装了普通io流对于文件的处理

使用方法:需要导入第三方jar包

如何导jar包:
a.在当前module下,创建一个文件夹->取名为lib或者libs
b.将想要使用的jar包复制到lib下
c.解压jar包:
对着lib右键->add as Library->在leve处,选择module(上面的name栏空了,不用管)->点ok

IOUtils类
- 静态方法:IOUtils.copy(InputStream in,OutputStream out)传递字节流,实现文件复制。
- 静态方法:IOUtils.closeQuietly(任意流对象)悄悄的释放资源,自动处理close()方法抛出的异常。
  IOUtils.copy(new FileInputStream("E:\\Idea\\io\\24.jpg"),new FileOutputStream("E:\\Idea\\io\\hahah.jpg"));
拷贝一个文件
FileUtils类

- 静态方法:FileUtils.copyDirectoryToDirectory(File src,File dest);
           传递File类型的目录,进行整个目录的复制,自动进行递归遍历。
           
           参数:
             src:要复制的文件夹路径
             dest:要将文件夹粘贴到哪里去
             
- 静态方法:writeStringToFile(File file,String str)写字符串到文本文件中。
- 静态方法:String readFileToString(File file)读取文本文件,返回字符串。
    public static void main(String[] args)throws Exception {
        //method01();
        //method02();
        method03();
    }


    /**
     *   - 静态方法:String readFileToString(File file)读取文本文件,返回字符串。
     */
    private static void method03()throws Exception {
        String s = FileUtils.readFileToString(new File("day21_network\\io\\1.txt"));
        System.out.println(s);
    }

    /**
     *   - 静态方法:writeStringToFile(File file,String str)写字符串到文本文件中。
     *   file:要存的文件的路径
     *   str:要保存的数据
     */
    private static void method02() throws IOException {
        FileUtils.writeStringToFile(new File("day21_network\\io\\1.txt"),"彤彤最好看");

    }


    /**
     * - 静态方法:FileUtils.copyDirectoryToDirectory(File src,File dest);
     *            传递File类型的目录,进行整个目录的复制,自动进行递归遍历。
     *
     *            参数:
     *              src:要复制的文件夹路径
     *              dest:要将文件夹粘贴到哪里去
     */
    private static void method01()throws Exception {
        FileUtils.copyDirectoryToDirectory(new File("E:\\Idea\\io\\aa"),new File("E:\\Idea\\io\\bb"));

    }
}

2、Lombok的使用

Lombok能通过注解的方式,在编译时自动为属性生成构造器、getter/setter、equals、hashcode、toString方法。

但是他不能自动创建有参的构造方法,如果手动的创建有参构造方法,他提供的无参就会消失

添加lombok的jar包:lombok-1.18.8.jar,导入后还需要联网进行下载lombok

1.@Getter和@Setter

  • 作用:生成成员变量的get和set方法。
  • 写在成员变量上,指对当前成员变量有效。
  • 写在类上,对所有成员变量有效。
  • 注意:静态成员变量无效。

2.@ToString

  • 作用:生成toString()方法。
  • 注解只能写在类上。

3.@NoArgsConstructor和@AllArgsConstructor

  • @NoArgsConstructor:无参数构造方法。
  • @AllArgsConstructor:满参数构造方法。
  • 注解只能写在类上。

4.@EqualsAndHashCode

  • 作用:生成hashCode()和equals()方法。
  • 注解只能写在类上。

5.@Data

  • 作用:生成get/set,toString,hashCode,equals,无参构造方法
  • 注解只能写在类上。

3.网络编程

C/S结构 :全称为Client/Server结构,是指客户端和服务器结构。常见程序有QQ,需要下载客户端进行使用

B/S结构 :全称为Browser/Server结构,是指浏览器和服务器结构。常见浏览器有IE

服务器的概念:安装了服务器软件的高性能计算机,才能叫做服务器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nLuH0Utl-1658415050661)(/D:/Typora/bin/document/img/1656294668492.png)]

客户端和服务器的交互(客户端发请求给服务器,服务器通过处理请求,将得到的响应反馈给客户端)

1.通信三要素

特殊的网址:代表的是本机地址,到了哪里都不会变,代表自己
127.0.0.1
localhost

TCP:面向连接协议
需要先确认连接,才能进行数据交互
三次握手:

        - 第一次握手,客户端向服务器端发出连接请求,等待服务器确认。
                    - 第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求。
                    - 第三次握手,客户端再次向服务器端发送确认信息,确认连接。

     好处:数据安全,能给数据的传输提供一个安全的传输环境
     坏处:效率低


UDP:面向无连接协议
​ 好处:效率高
​ 坏处:传输的数据不安全,容易丢失数据包

[端口号]
每一个应用程序的唯一标识

用两个字节表示的整数,它的取值范围是065535。其中,01023之间的端口号用于一些知名的网络服务和应用,普通的应用程序需要使用1024以上的端口号

1.三次握手:
  • 第一次握手,客户端向服务器端发出连接请求,等待服务器确认。
  • 第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求。
  • 第三次握手,客户端再次向服务器端发送确认信息,确认连接。
2.四次挥手:

​ 第一次挥手:客户端向服务器端提出结束连接,让服务器做最后的准备工作。

第二次挥手:服务器接收到客户端释放连接的请求后,会将最后的数据发给客户端。并告知上层的应用进程不再接收数据。

第三次挥手:服务器发送完数据后,会给客户端发送一个释放连接的报文。

第四次挥手:客户端接收到服务器最后的释放连接报文后,要回复一个彻底断开的报文。

2.实现简单客户端和服务端的交互

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qL5D30pU-1658415050662)(/D:/Typora/bin/document/img/1656295194478.png)]

1.编写客户端

1.创建Socket对象,设置服务端的IP和端口号
2.调用getOutputStream,用于往服务端发送请求(写数据)
3.调用getInputStream,用于读取服务器响应回来的结果(读数据)
4.关闭资源
/**
 * 客户端
 */
public class Client {
    public static void main(String[] args)throws Exception {
        //1.创建Socket对象,设置服务端的IP和端口号
        Socket socket = new Socket("127.0.0.1", 6666);
        //2.调用getOutputStream,用于往服务端发送请求(写数据)
        OutputStream os = socket.getOutputStream();
        os.write("我想下载个片儿".getBytes());
        //3.调用getInputStream,用于读取服务器响应回来的结果(读数据)
        InputStream is = socket.getInputStream();
        byte[] bytes = new byte[1024];
        int len = is.read(bytes);
        System.out.println(new String(bytes,0,len));
        //4.关闭资源
        is.close();
        os.close();
        socket.close();

    }
}

2.编写服务端

1.创建ServerSocket对象,设置端口号
2.调用accept方法监听哪个Socket连接
3.调用getInputStream,用于读取客户端发来的请求(读数据)
4.调用getOutputStream,用于往客户端发送响应(写数据)
5.关闭资源
/**
 * 服务端
 */
public class Server {
    public static void main(String[] args)throws Exception {
        //1.创建ServerSocket对象,设置端口号
        ServerSocket ss = new ServerSocket(6666);
        //2.调用accept方法监听哪个Socket连接
        Socket socket = ss.accept();
        //3.调用getInputStream,用于读取客户端发来的请求(读数据)
        InputStream is = socket.getInputStream();
        byte[] bytes = new byte[1024];
        int len = is.read(bytes);
        System.out.println(new String(bytes,0,len));
        //4.调用getOutputStream,用于往客户端发送响应(写数据)
        OutputStream os = socket.getOutputStream();
        os.write("给你一个片儿".getBytes());
        //5.关闭资源
        os.close();
        is.close();
        socket.close();
        ss.close();
    }
}

3.文件上传

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f6pYmXw9-1658415050663)(/D:/Typora/bin/document/img/1656295370507.png)]

4.b/s架构

/**
 * 问题:如何将InputStream转成BufferedReader
 * 

* 怎么转:我们只要想办法将InputStream塞到BufferedReader的构造中就可以了 *

* 怎么塞: * BufferedReader(Reader in) * Reader可以接收FileReader(String path) * Reader可以接收InputStreamReader(InputStream in) *

* new BufferedReader(new InputStreamReader(inputStream)) */ public class BsServer { public static void main(String[] args) throws IOException { //1.创建ServerSocket ServerSocket ss = new ServerSocket(8888); while(true){ //2.调用accept接收连接的客户端对象 Socket socket = ss.accept(); //3.调用getInputStream,读取浏览器(客户端)发过来的请求 InputStream is = socket.getInputStream(); //4.将InputStream转成BufferedReader BufferedReader br = new BufferedReader(new InputStreamReader(is)); String path = br.readLine();// GET /day21_network/web/index.html HTTP/1.1 //5.将day21_network/web/index.html获取出来 String[] arr = path.split(" "); String s = arr[1]; String filePath = s.substring(1);// day21_network/web/index.html //6.创建FileInputStream,根据解析出来的路径将本地上的html读到内存中 FileInputStream fis = new FileInputStream(filePath); //7.边读编写 OutputStream os = socket.getOutputStream(); //把响应信息先写给浏览器 os.write("HTTP/1.1 200 OK\r\n".getBytes()); os.write("Content-Type:text/html\r\n".getBytes()); os.write("\r\n".getBytes()); byte[] bytes = new byte[1024]; int len = 0; while((len = fis.read(bytes))!=-1){ os.write(bytes,0,len); } //8.关闭资源 os.close(); fis.close(); br.close(); is.close(); socket.close(); } } }

day22(6.26)

今日重点:
  1.会编写lambda表达式
  2.会使用Stream流中的常用方法

1、函数式编程思想和Lambda表达式定义格式

1、函数式思想

1.面向对象思想: 很多事情自己不想做,调用别人的功能帮我去做事
              为什么要创建对象,因为我们不创建对象,就调用不了这个对象的功能
              过程:new对象    目的:使用对象中的方法
                  
2.函数式编程思想:
  只注重目的(方法),不注重过程(new对象),谁不注重,就可以干掉谁
 

2、Lamdba表达式

1.适用前提:
1.前提:
  函数式接口做方法参数传递
      
2.函数式接口:必须有且只能有一个抽象方法的接口
  
3.可以使用注解:@FunctionalInterface检测该接口是否为函数式接口
2.lambda表达式的格式:
lambda表达式的格式:
  ()->{}

  ():重写方法的参数位置
  ->:将参数传递到方法体中
  {}:重写方法的方法体位置
-------------------------------------------------
public class Test01 {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("我要执行了");
            }
        }).start();

        System.out.println("========================");

        new Thread(()-> {
                System.out.println("我要执行了");
        }).start();

        System.out.println("========================");

        new Thread(()-> System.out.println("我要执行了")).start();

    }
}
3.Lambda表达式省略规则

1.观察是否是函数式接口做方法参数传递
2.如果是,考虑用lambda表达式
3.调用方法,传递实参的时候,写成匿名内部类的形式
4.从new接口开始,到重写方法的方法名结束->选中->删除
(不要忘记将右半个大括号也干掉)
5.在重写方法的参数后面,方法体前面加上 ->
6.再看看能不能根据省略规则再次删一删

lambda表达式省略规则:
1.重写方法的参数类型可以省略
2.如果重写方法只有一个参数,那么类型可以干掉,所在的小括号也可以干掉
3.如果重写方法的方法体只有一句话,所在的方法体大括号可以干掉,方法体中的那一句分号可以干掉
4.如果重写方法的方法体只有一句话,且还是带return的,那么return,方法体的大括号,分号都可以干掉

3.函数式接口

有且仅有一个抽象方法的接口,通过@FunctionalInterface->检测

1.定义格式:

  @FunctionalInterface
  public interface 接口名{
      抽象方法
  }
@FunctionalInterface
public interface USB {
    void open(String s);
}

-----------------------------------------------
public class Test01 {
    public static void main(String[] args) {
        get(new USB() {
            @Override
            public void open(String s) {
                System.out.println(s+"开启了");
            }
        });

        System.out.println("==========lambda==========");
        get((String s)-> {
                System.out.println(s+"开启了");
        });

        System.out.println("==========lambda简化==========");

        get(s-> System.out.println(s+"开启了"));
    }

    public static void get(USB usb){
        usb.open("鼠标");
    }
}

2.java提供的几个常用接口

1.Supplier
  java.util.function.Supplier接口,它意味着"供给"->我们想要什么就给什么
2.方法:
  T get()

3.需求:
   使用Supplier接口作为方法的参数
   用Lambda表达式求出int数组中的最大值
public class Test02_Supplier {
    public static void main(String[] args) {
        method(new Supplier<Integer>() {
            @Override
            public Integer get() {
                //定义数组
                int[] arr = {3,2,4,3,5,6};
                Arrays.sort(arr);
                return arr[arr.length-1];
            }
        });

        System.out.println("=======================");

        method(()->{
                //定义数组
                int[] arr = {3,2,4,3,5,6};
                Arrays.sort(arr);
                return arr[arr.length-1];
        });

    }

    public static void method(Supplier<Integer> supplier){
        Integer result = supplier.get();
        System.out.println("result = " + result);
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pQ7kHMOa-1658415050665)(/D:/Typora/bin/document/img/1656296504112.png)]

2.Consumer
java.util.function.Consumer<T>->消费型接口
  方法:
    void accept(T t),意为消费一个指定泛型的数据
        
"消费"即使"操作"
public class Test03_Consumer {
    public static void main(String[] args) {
        method(s-> System.out.println(s.toUpperCase()),"abcdefg");
    }

    public static void method(Consumer<String> consumer,String s){
        consumer.accept(s);
    }
}
3.Function
java.util.function.Function接口用来根据一个类型的数据得到另一个类型的数据
  方法:
     R apply(T t)根据类型T参数获取类型R的结果
 public static void main(String[] args) {
        method(new Function<Integer, String>() {
            @Override
            public String apply(Integer integer) {
                return 100+"";
            }
        },100);

        System.out.println("====================");
        method(integer-> 100+"",100);

    }

    public static void method(Function<Integer,String> function,Integer i){
        String s = function.apply(i);
        System.out.println(s+1);
    }
4.Predicate
java.util.function.Predicate接口。->判断型接口
    boolean test(T t)->用于判断的方法,返回值为boolean型

4.Stream流

1.数组和集合的获取:

1.针对数组:
  static <T> Stream<T> of(T... values)  
2.针对集合:Collection中有一个方法
  Stream<E> stream()  

2.方法:

1.Stream中的forEach方法:void forEach(Consumer action);
forEach : 逐一处理->遍历
void forEach(Consumer action);

注意:forEach方法再Stream流中是一个终结方法
2.Stream中的long count()方法
1.作用:统计元素个数
2.注意:count方法 也是一个终结方法(只能使用一次,用过之后就销毁了)
3.Stream中的Stream filter(Predicate predicate)方法
1.方法:Stream<T> filter(Predicate<? super T> predicate)方法,返回一个新的Stream流对象
2.作用:根据某个条件进行元素过滤
4.Stream limit(long maxSize):获取Stream流对象中的前n个元素,返回一个新的Stream流对象
1.Stream<T> limit(long maxSize):获取Stream流对象中的前n个元素,返回一个新的Stream流对象
5.Stream skip(long n): 跳过Stream流对象中的前n个元素,返回一个新的Stream流对象
Stream<T> skip(long n): 跳过Stream流对象中的前n个元素,返回一个新的Stream流对象
6.static Stream concat(Stream a, Stream b):两个流合成一个流
1.方法:static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b):两个流合成一个流
7.将Stream流变成集合
Stream流对象转成集合对象,使用Stream接口方法collect
public class Test10_Collect {
    public static void main(String[] args) {
        Stream<String> stream1 = Stream.of("张无忌", "张三丰", "张翠山", "张三", "李四", "王五", "赵六", "田七");

        List<String> list = stream1.collect(Collectors.toList());
        System.out.println(list);
    }
}

1.Stream流的使用场景:当处理集合中的元素,为了方便,可以考虑使用stream流

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dKy0euqC-1658415050665)(/D:/Typora/bin/document/img/1656297333913.png)]

5.方法的引用

方法引用的介绍

1.概述:在lambda表达式简化格式的基础上再次简化
2.前提:条件更苛刻
  a.被引用的方法,必须写在重写的方法中
  b.被引用的方法,从参数上,返回值上,参数个数上,参数类型上都要和所在的重写的方法保持一致
3.怎么引用:
  在lambda表达式最简化的基础上-> 干掉参数,干掉->,干掉被引用的方法参数
  将.换成::
public class Test01 {
    public static void main(String[] args) {
       /* method(new Supplier() {

            *//*
              get:无参,返回值类型为String

              toUpperCase:
                1.在重写的get方法中使用
                2.无参
                3.返回值类型为String
             *//*
            @Override
            public String get() {
                return "abcdefg".toUpperCase();
            }
        });*/

        System.out.println("=====================");
        //method(()-> "abcdefg".toUpperCase());

        System.out.println("===========方法引用==========");

        method("abcdefg"::toUpperCase);
    }

    public static void method(Supplier<String> supplier){
        String s = supplier.get();
        System.out.println(s);
    }
}

1.对象名–引用成员方法

1.使用对象名引用成员方法
2.格式:
  对象::成员方法名
public static void main(String[] args) {
        Stream<String> stream = Stream.of("张三", "李四", "王五", "赵六");

        /*
            accept:
              1.有一个String型的参数
              2.没有返回值

            println:
              1.在重写的accept方法中使用
              2.有一个String型的参数
              3.没有返回值
         */
       /* stream.forEach(new Consumer() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });*/

        System.out.println("=========lambda表达式=========");

        //stream.forEach(s-> System.out.println(s));

        System.out.println("=========方法引用=========");

        //对象::成员方法名
        stream.forEach(System.out::println);
    }

2.类名–引用静态方法

格式:
 类名::静态方法名

3.类–构造引用

1.格式: 类名 对象名 = new 构造方法名()
  构造方法名::new

4.数组–数组引用

1.数组创建:    int[] arr = new int[3]
  可以变相的将:
    int看成方法名
    [3]看成方法参数
    int[]看成返回值类型    
2.数组引用:
  数组类型[]::new
      
  int[]::new  创建一个int型的数组
 public static void main(String[] args) {

        /*
          apply:
            1.有一个Integer型的参数
            2.有一个int[]的返回值类型

          int[integer]:
            1.在重写的apply中使用
            2."参数"->Integer
            3."返回类型"->int[]
         */
       /* method(new Function() {
            @Override
            public int[] apply(Integer integer) {
                return new int[integer];
            }
        },5);*/

        System.out.println("====================");

        //method(integer-> new int[integer],5);

        System.out.println("==========方法引用===========");
        method(int[]::new ,5);
    }

    public static void method(Function<Integer,int[]> function,Integer i){
        int[] arr = function.apply(i);
        System.out.println(arr.length);
    }
}

day23(6.28)反射

1、Junit单元测试

1.介绍

1.概述:Junit是一个Java语言的单元测试框架,简单理解为在一定程度上用于取代main方法,Junit属于第三方工具,需要导入jar包后再使用
2.作用:可以单独去运行一个我们想要测试的功能,不用提供main方法了
3.导jar包:
  a.在模块下创建文件夹:lib
  b.将要使用的第三方jar包,粘贴到lib下
  c.对着lib,右键->add as library->module level ->ok 

2.基本使用

1.注意:需要在方法上写,不要在类上写
       @Test
2.怎么执行单元测试的方法:
  a.选中要执行的方法名,右键->run
  b.选择方法左边的绿色小箭头->run
  c.如果想要执行当前类中所有带@Test的单元测试方法,点类名左边绿色小箭头->run

3.注意事项

1.@Test修饰的方法,不能有参数
2.@Test修饰的方法,不能有返回值
3.@Test修饰的方法,不能是静态的
    
一般情况下,我们都会单独定义一个方法,在此方法中使用单元测试,在方法体中调用要测试的方法

4.相关注解

@Before:@Test之前执行,有多少个@Test一起执行,@Before就执行多少次-> 可以用来初始化一下参数
@After:@Test之后执行,有多少个@Test一起执行,@After就执行多少次->可以用来关闭资源
    
@Before:可以做变量的初始化

@After:可以做关闭资源操作

5.使用方法

1.直接执行一个要被测试的方法,可以直接在该方法上加@Test
2.单独创建一个测试类,专门测试某一个模块的功能
  在测试类中定义测试方法,加上@Test.在方法中调用要测试的模块功能

2、类的加载时机

1new 对象

2new子类(new 子类,先进行父类构造初始化)

3、执行main方法

4、调用静态成员

5、利用反射,反射这个类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GSL3gAHS-1658415050667)(/D:/Typora/bin/document/img/1656412298092.png)]

类加载器(ClassLoader):jvm中将本地的class文件加载到内存的对象

不同的类加载器负责加载不同的类

1BootStrapClassLoader:根类加载器->负责java核心类的加载。如SystemString
2ExtClassLoader:扩展类加载器->负责加载扩展类
3AppClassLoader:系统类加载器->负责加载自定义类和第三方jar包

双亲委派+缓存机制-->类只加载一次

3、反射

1、概述:反射就是解剖class对象
2、class对象和class类
	class对象:jvm为加载到内存的class文件创造出来的对象
	class类:描述class对象的类

1.获取class对象的几种方式:

1)通过调用object类的getClass()

2)通过调用静态成员(每一个类型都具有的属性):class

3)使用Class类的方法:foeName

static Class<?> forName(String className) 

其中className为类的权限定名(包名.类名)

写完类的全限定名之后,怎么验证写对了:

按住ctrl不放,去点对应的类名,如果能跳过去,证明类的全限定名写对了

以后,在配置文件中写类的全限定名,也可以用以上操作去验证是否写对了

1)案例《通过反射获取properties文件》
1.static Class forName(String className)
  className:类的全限定名 -> 包名.类名
    
2.为啥:因为参数为字符串类型,可以配合properties文件使用
    
3.如何创建properties配置文件
  xxx.properties
4.properties配置文件内容写法:
  a.必须是key=value形式
  b.key和value都是String类型,但是不要加双引号
  c.每一个key=value写完要换行写下一对key=value
  d.不要有空格
  e.不要有中文
userName=com.atgug.reflect.Student
    
    				--db.properties
------------------------------------
public static void main(String[] args) throws Exception {
        //创建Properties对象
        Properties pp=new Properties();
        //从指定路径读取数据到内存中
        FileInputStream fis=new FileInputStream("day23/db.properties");
        //将读取到的数据放到prperties集合中
        pp.load(fis);
        //通过userName(key)获得对应的值
        String Name = pp.getProperty("userName");
        System.out.println(Name);
        //获得所对应的class对象
        Class<?> aClass = Class.forName(Name);
        System.out.println(aClass);
    }

2、获取Class对象中的构造方法

1)获取所有的public构造方法
Class类中的方法:
   Constructor<?>[] getConstructors() -> 获取所有的public的构造方法
2)获取空参的构造方法
Class类中的方法:
  Constructor getConstructor(Class... parameterTypes) ->获取指定构造 
    parameterTypes:为可变参数,可以传递0个或者多个参数。如果是无参则不写,如果是有参,则写对应构造参数类型的class对象【如String.class】
  
 ----------------------------------
 通过获得的构造方法创建对应的对象
 Constructor类中的方法:
  T newInstance(Object... initargs)  -> 根据获取出来的Constructor创建对应的对象
  参数说明:initargs->可变参数 -> 可以传递0个或者多个参数
         传递的是实参
3)通过无参快速创建对象的快捷方式
Class类中的方法:
  T newInstance()-> 根据空参构造创建对象
      
前提:被反射的类中必须有空参构造
4)用反射获取有参构造方法并创建对象(public)
Class类中的方法:
  Constructor getConstructor(Class... parameterTypes) ->获取指定构造 
                                 parameterTypes:可变参数,可以传递0个或者多个参数
                                 如果获取空参构造:parameterTypes不写了
                                 如果获取有参构造:parameterTypes写的是构造参数类型的class对象
                                     
Constructor类中的方法:
  T newInstance(Object... initargs)  -> 根据获取出来的Constructor创建对应的对象
  参数说明:initargs->可变参数 -> 可以传递0个或者多个参数
         传递的是实参

5)利用反射获得私有方法(暴力反射)

获取所有的构造->包含public,也包含privateClass类中的方法:
  Constructor<?>[] getDeclaredConstructors()  ->  获取所有构造,包含private
获取指定的构造->包含private
1.class类中的方法:
  Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)  
                                 parameterTypes:可变参数,可以传递0个或者多个参数
                                 如果获取空参构造:parameterTypes不写了
                                 如果获取有参构造:parameterTypes写的是构造参数类型的class对象
                                     

                                     
2.Constructor类中的方法:
  T newInstance(Object... initargs)   -> 根据获取出来的Constructor创建对应的对象
  参数说明:initargs->可变参数 -> 可以传递0个或者多个参数
         传递的是实参
      
      
3.获取出私有成员,不能直接使用,但是我们是反射技术,这个技术太吊了,既然能获取,就一定能使用
  Constructor有一个父类->AccessibleObject

4.AccessibleObject类中有一个方法:
  void setAccessible(boolean flag)  -> flag如果设置为true -> 解除私有权限(暴力反射)
public static void main(String[] args) throws Exception {
        Class<Student> sc = Student.class;
        Constructor<Student> dc = sc.getDeclaredConstructor(String.class);
        System.out.println(dc);
        dc.setAccessible(true);
        Student student = dc.newInstance("哈哈");
        System.out.println(student);
    }

3、反射方法

1)获取所有的成员方法
Class类中的方法:->都是public的->连父类中的public方法都能拿到
  Method[] getMethods()
2)获取方法(有参、无参)
1.Class类中的方法:
   Method getMethod(String name, Class<?>... parameterTypes)  
                    name:要获取方法名
                    parameterTypes:指定方法具体的参数类型,是一个Class对象  
                                   如果获取的方法没有参数,此处空着
                        
2.Method类中的方法:
  Object invoke(Object obj, Object... args)  ->执行被反射出来的方法
               a.参数说明:
                 obj:根据构造创建出来的对象
                 args:传递的是实参 如果调用的方法没有参数,args就不写了
               b.返回值说明:Object
                 接收的是被调用方法的返回值
                 如果调用的方法没有返回值,那么就不用Object接收了
                 如果调用的方法有返回值,那么就用Object接收一下吧
   
3)小练习
利用反射,解析配置文件中的信息:properties文件
  类的全限定名:  className=包名.类名
  方法名:       methodName=方法名
  
需求:利用反射解析properties文件,获取配置信息
    根据配置信息,执行配置文件中的方法
  
步骤:
  1.创建Properties配置文件,写配置信息
    a.问题1:配置文件将来要放到哪里?
      我们将来给用户的都是out路径下的class文件,如果我们将配置文件直接放到模块下,out路径下是没有配置文件生成的,那么将class文件给用户,用户一执行,发现找不到配置文件中的数据,肯定执行不了
        
    b.解决问题1:我们应该将配置文件放到src下
        
    c.问题2:如果将配置文件放到src下,你怎么写读取路径
      FileInputStream in = new FileInputStream("day23_reflect\\src\\bean.properties");
  
    d.解决问题2:类加载器
      ClassLoader classLoader = 当前类.class.getClassLoader()
      classLoader.getResourceAsStream("配置文件名称.后缀名") -> 自动扫描src下的配置文件
     
    e.问题3:将来我们可能有很多很多配置文件,难道我们都要放到src下吗?这样不乱吗?
    f.解决问题3:在当前模块下创建一个文件夹,取名为:resources -> 将其变成源目录->将配置文件放到此路径下
      
    
  2.利用Properties集合配合IO流解析配置文件,将配置文件中的value获取出来
  3.根据配置文件中的类的全限定名获取Class对象
  4.根据配置文件中的方法名反射对应的方法
  5.调用invoke执行该方法

代码:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-84Yp5yqC-1658415050669)(/D:/Typora/bin/document/img/1656417795168.png)]

userName=com.atgug.reflect.Person
runName=method
public class Person {
    public void method(){
        System.out.println("哈哈哈");
    }
 public static void main(String[] args) throws Exception {
        //创建properties对象
        Properties pp=new Properties();
        //利用类加载器读取配置文件
        InputStream rs = Reflectemo08.class.getClassLoader().getResourceAsStream("db.properties");
        pp.load(rs);
        //通过键找值
        String userName = pp.getProperty("userName");
        String runNname = pp.getProperty("runName");
//        System.out.println(userName);
//        System.out.println(runNname);
        //获取class对象
        Class<?> aClass = Class.forName(userName);
        //通过获取的对象创建对象
        Object o = aClass.newInstance();
        //获得特定的方法
        Method method = aClass.getMethod(runNname);
        //调用方法
        method.invoke(o);
       // System.out.println(invoke);

    }

4、注解

1.介绍

1.jdk1.5版本的新特性->一个引用数据类型
       和类,接口,枚举是同一个层次的
     
        引用数据类型:类 数组  接口 枚举 注解
 2.作用:
        说明:对代码进行说明,生成doc文档(API文档)(不会用)
        检查:检查代码是否符合条件   @Override(会用) @FunctionalInterface
        分析:对代码进行分析,起到了代替配置文件的作用(会用)
        
3.JDK中的注解:
        @Override  ->  检测此方法是否为重写方法
           jdk1.5版本,支持父类的方法重写
           jdk1.6版本,支持接口的方法重写
        @Deprecated -> 方法已经过时,不推荐使用
                       调用方法的时候,方法上会有横线,但是能用
        @SuppressWarnings->消除警告  @SuppressWarnings("all")

2.注解的定义以及属性的定义格式

1.定义格式:
  public @interface 注解名{
      
  }
2.定义属性(增强注解作用)
  数据类型 属性名()   -> 不带默认值,需要在使用注解的时候给此属性赋值
  数据类型 属性名() default 值 -> 带默认值的,在使用注解的时候无需单独为其赋值,要是想赋新的值,也可以赋
    
3.注解中能定义什么类型的属性呢?
  a.8种基本类型(byte short int long  float double char boolean)
  b.String类型 class类型 枚举类型 注解类型
  c.以上类型的一维数组

3.注解的使用

1.使用注解(实质上就是为注解中的属性赋值)
2.使用位置:
  类上,方法上,参数上,属性上,构造上 等
3.怎么使用:
  @注解名(属性名 =,属性名 =)
  @注解名(属性名={元素1,元素2})    
public @interface Book {
    //书名
    String name();
    //价格
    int price();
    //作者
    String[] author();
    //数量
    int count() default 10;
}
@Book(name="金瓶梅",price=9,author={"金莲","涛哥"})
public class BookShelf { 
}

4.注解注意事项:

​ 1.空注解可以直接使用->空注解就是注解中没有任何的属性
​ 2.不同的位置可以使用一样的注解,但是同样的位置不能使用一样的注解
​ 3.使用注解时,如果此注解中有属性,注解中的属性一定要赋值,如果有多个属性,用,隔开
​ 如果注解中的属性有数组,使用{}
​ 4.如果注解中的属性值有默认值,那么我们不必要写,也不用重新赋值,反之必须写上
​ 5.如果注解中只有一个属性,并且属性名叫value,那么使用注解的时候,属性名不用写,直接写值
​ (包括单个类型,还包括数组)

5、注解解析的方法->AnnotatedElement接口

解析思想:
   1.获取Class对象,或者Method对象
   2.判断类上或者方法上有没有注解
   3.如果有注解,获取注解
   4.获取到注解就可以获取注解中的属性值
1.什么叫解析注解:将注解中的属性值获取出来
2.使用:  AnnotatedElement接口
        实现类:Method,Field,Constructor,class3.解析方法:
  boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)->判断对应的位置上有没有注解
           annotationClass:写的是注解的class对象
               
  a.比如:判断BookShelf类上有没有Book注解:
        Class class = BookShelf.class
        class.isAnnotationPresent(Book.class)
            
            
  getAnnotation(Class<T> annotationClass)->获取指定的注解
           annotationClass:写的是注解的class对象
  a.比如:获取BookShelf类上的Book注解:
    Class class = BookShelf.class
    class.isAnnotationPresent(Book.class)
    class.getAnnotation(Book.class)

5、元注解

1.概述:管理注解的注解
2.元注解都是从哪些方面管理注解
  a.使用位置上:控制注解是否能在类上使用,是否能在方法上使用,是否能在参数上使用等
  b.生命周期上:控制注解只存在在源码中,控制注解是否能存在在class文件中,控制注解是否能存在在内存中
      
3.常见的元注解:
  a.Target:控制注解能放到哪个位置上
    ElementType[] value()-> ElementType是一个枚举类->枚举类中的成员可以类名直接调用
    TYPE:控制注解能使用在类上
    FIELD:控制注解能使用在成员变量上
    METHOD:控制注解能使用在方法上
    PARAMETER:控制注解能使用在参数上
    CONSTRUCTOR:控制注解能使用在构造上
    LOCAL_VARIABLE:控制注解能使用在局部变量上
        
  b.Retention:控制注解的生命周期
    RetentionPolicy value();->RetentionPolicy是一个枚举类->枚举类中的成员可以类名直接调用
    SOURCE:控制注解只能在源码中出现 -> 默认
    CLASS:控制注解可以在class文件中
    RUNTIME:控制注解可以在内存中

解析代码(两次)

//第一次解析
解析思想:
   1.获取Class对象,或者Method对象
   2.判断类上或者方法上有没有注解
   3.如果有注解,获取注解
   4.获取到注解就可以获取注解中的属性值
       -------------------

public @interface Test {
    String name();
    int age();
    String id() default "10";
    String[] arg();
}

------------------------
public class Reflectemo05 {
        public static void main(String[] args) {
            Class<Reflectemo04> rf = Reflectemo04.class;
            boolean ap = rf.isAnnotationPresent(Test.class);
            if(ap){
                Test an = rf.getAnnotation(Test.class);
                System.out.println(an.name());
                System.out.println(an.age());
                System.out.println(an.id());
                System.out.println(Arrays.toString(an.arg()));
            }
        }
}
@Test(name="哈哈",age=12,arg={"hh","ja"})
public class Reflectemo04 {
        public static void main(String[] args) {
        
    }
}

第二次解析

@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
    String name();
    int age();
    String id() default "10";
    String[] arg();
}

第一次解析,没有解析出来,因为Book注解默认在源码中,不在内存中

第二次解析,解析出来了,因为Book注解在内存中了,所以我们能从内存中获取Book注解

你可能感兴趣的:(java基础+进阶,java,intellij-idea)