java小白第二天
- 00 写在前面
- 01 面向对象
- 01 类和对象
- 类的相关总结
- 02 就进原则和this
- 03 构造方法
- 04 对象内存图
- 01 一个对象的内存图
- 02 多个对象的内存图
- 03 两个变量(引用)指向同一个对象的内存图
- 04 this的内存原理
- 05 基本数据类型和引用数据类型的区别
- 06 局部变量和成员变量的区别
- 03 面向对象的三大法宝
- 01 封装
- 01 private
- 02 继承 -- 类与类中的父子关系
- 01 子类到底能继承父类中的哪些内容
- 02 继承中对象访问特点
- 01 继承中成员变量访问特点
- 02 继承中成员方法访问特点
- 03 继承中构造方法访问特点
- 03 多态
- 01 多态调用成员的特点
- 小结
- 面向对象进阶
- 01 static -- 共享
- 01 static 静态对象(成员变量与成员方法)内存图
- 02 小结
- 02 认识main方法
- 03 包
- 04 final 关键字
- 05 权限修饰符
- 06 代码块
- 01 局部代码块 -- 淘汰
- 02 构造代码块 -- 淘汰
- 03 静态代码块
- 07 abstract
- 08 interface
- 01 接口的定义和使用
- 02 接口中成员的特点
- 03 接口和类之间的关系
- 04 接口扩展
- 01 jdk8开始接口中新增的方法
- 02 接口应用
- 03 3适配器设计模式
- 09 内部类
- 01 内部类分类
- 10 浅克隆和深克隆
- 11 转义字符
- 11 正则表达式 Regex
- 01 校验字符串
- 02 爬虫
- 03 正则表达式在字符串方法中的使用
- 注意事项
- 01 非捕获分组--代码示例
- 02 分组 ()
- API
- 01 字符串
- 01 String
- 01 Stirng 方法
- 02 StringBuilder
- 01 构造方法
- 02 常用方法
- 03 StringJoiner
- 01 构造方法
- 02 成员方法
- 04 字符串底层原理
- 02 ArrayList--集合
- 01 ArrayList
- 03 System
- 04 Runtime
- 05 Object
- 06 Objects
- 07 BigInteger
- 01 构造方法
- 02 成员方法
- 08 BigDecimal
- 构造方法
- 成员方法
- 09 Date
- 01 jdk7前时间相关类
- 02 jdk8新增时间相关类
- 01 ZoneID
- 02 Instant 时间戳
- 03 ZoneDateTime
- 04 DateTimeFormatter
- 05 LocalDate LocalTime LocalDateTime
- 06 Duration Period ChronoUnit
- 10 包装类
- 10 Arrays
- 插件
- 案例 -- 拼图小游戏
00 写在前面
本文是边看黑马b站视频边写的一片笔记, 文中大多图片都来自黑马视频. 旨在巩固学习以及方便后续查阅和供广大朋友们学习, 感谢黑马视频分享
01 面向对象
内容目录架构
01 类和对象
- 类: 是对一类具有相似属性和行为的事物的抽象
- 对象: 类的实例化, 具体的一个实体对象
都有什么类?
- javabean: 用来描述一类事物的类. (有成员变量, 方法等) 此类中不写main方法
- 测试类: 用来检查其他类是否书写正确, 带有main方法的类, 是程序的入口
- 工具类: 不是用来描述一类事物的, 而是帮我们做一些事情的类
- 私有化构造方法
public class Tool{
private Tool(){
}
}
- 方法定义为静态
类定义格式
类的五大成员: 属性 方法 构造方法 代码块 内部类
public class 类名{
1. 成员变量
2. 静态变量
3. 成员方法
4. 静态方法
5. 构造器
6. 代码块
7. 内部类
}
public class 类名{
1. 成员变量
数据类型 变量名;
修饰符 数据类型 变量名 = 变量值;
2 静态变量 -- 属于类的 所有实例化对象共享
public static String school;
3. 成员方法
public 返回值类型 方法名(参数列表){
方法体
return 返回值;
}
4. 静态方法 -- 属于类的 所有实例化对象共享
public static 返回值类型 方法名(参数列表){
方法体
return 返回值;
}
3. 构造器
4. 代码块
5. 内部类
}
# 一个标准类**javabean**要求
1. 所有成员变量都用 private 修饰
2. 提供私有成员变量的 getter 和 setter 方法
3. 提供空参和全参的构造方法
4. 提供 toString() 方法 -- 使得类的对象名的输出不是地址值, 而是属性值
创建对象格式类名 对象名 = new 类名();
对象调用对象名.成员变量;
对象名.成员方法名();
note:
- 类名须是大驼峰命名格式
- 一个java文件中可以由多个类, 但只能有一个类被public修饰, 且文件名须和public修饰的类名一致
- 一般无需为成员变量赋值, 有默认值
数据类型 | 默认值 |
byte short int long | 0 |
float double | 0.0 |
char | " " 空格 |
boolean | false |
String等引用类型 | null |
类的相关总结
这部分是对类涉及到的各方面零碎知识总结
成员修饰符
修饰符名称 | 作用 | 备注 |
private | private修饰的成员只能在类中被直接访问 | 类中成员变量都用private修饰,使用,结合get,set方法使用 |
public | 所有对象都可访问 |
类中变量种类
- 成员变量
- 局部变量–方法中定义的变量
类中方法种类
- 成员方法
- 静态方法
- 构造方法–创建对象时给成员变量赋值
02 就进原则和this
就近原则 :类中的变量有成员变量和局部变量(方法中的变量),当两者同名时,谁离调用处近用谁
this : 区分成员变量和局部变量
03 构造方法
- 创建对象时,虚拟机自动调用,初始化成员变量
构造方法格式
修饰符 类名(参数列表){
方法体;
}
特点
- 方法名与类名相同
- 没有返回值类型
- 没有具体返回值–不能由 return 带回结果数据
- 创建对象时(new时),虚拟机自动调用
- 如果没有写构造方法,虚拟机自动添加一个空参构造方法
- 如果定义了构造方法,虚拟机将不会提供默认的空参构造方法
构造方法类型
1. 空参
public 类名(){
...
} // 变量使用默认值
2. 全参
public 类名(需要赋值的成员变量列表){
使用 this 关键字赋值
}
04 对象内存图
Java中jvm内存分配介绍
字节码文件原来在方法区中, 现在在元空间中, 当运行一个类时, 就会加载其字节码文件到内存方法区, 栈内存, 堆内存存储内容如下:
方法区: 加载字节码文件进入内存
栈: 方法运行时进入的内存, 方法中创建的变量也在这里
堆: 凡是 new 出来的东西都在堆内存中, 并开辟相应空间产生地址标记
01 一个对象的内存图
Student s = new Student();
创建上述一个对象时, 内存使用情况:
- 加载class文件–加载Student类的字节码文件
- 声明局部变量–s
- 在堆内存中开辟一个空间–new Student()
- 默认初始化
- 显示初始化
- 构造方法初始化
- 将堆内存中的地址值赋值给左边的局部变量
当栈中所有方法执行完毕, 弹出栈后, 堆内存对应的空间就没有变量指向, 此时, 堆内存对应的空间也即变为垃圾空间, 内存空间被收回
堆内存对应的空间也即变为垃圾空间, 内存空间被收回
02 多个对象的内存图
类的字节码文件只加载一次进内存
03 两个变量(引用)指向同一个对象的内存图
04 this的内存原理
- this的本质: 代表方法调用者实例对象的地址值
- this的作用: 区分局部变量和成员变量
this的内存图
05 基本数据类型和引用数据类型的区别
- 基本数据类型变量: 自己的空间中, 存储的是值
- 引用数据类型变量: 自己的空间中, 存储的是地址引用, 即地址值
06 局部变量和成员变量的区别
- 成员变量–类中方法外, 只要在类中方法外即可
- 局部变量–方法中
图示如下
03 面向对象的三大法宝
封装 继承 多态
01 封装
- 封装原则* 对象代表什么,就得封装对应的数据,并提供数据对应的行为
如:人关门,关门的方法应该封装在门类中
01 private
- private 权限修饰符
- 用于修饰成员(成员方法和变量)
- 被private修饰的成员只能在本类类中访问,即通过该类实例化的对象无法直接访问private修饰的成员
02 继承 – 类与类中的父子关系
减少代码冗余, 提高代码复用性
当类与类之间, 存在相同(共性)的内容, 并满足子类是父类中的一种, 就可以考虑使用继承, 来优化代码
01 继承格式:
public class Student extends Teacher{}
02 继承的特点
- java只支持单继承(一个类只能有一个直接父类), 不支持多继承, 但支持多层继承
- java中每一个类都直接或间接继承于 object 类
- 子类只能访问父类中非私有的成员
01 子类到底能继承父类中的哪些内容
父类中的内容大概分为三类如下:
构造方法
成员变量
成员方法
进一步说明
01 构造方法不能被继承
子类不能继承父类构造方法
02 成员变量 – 子类可以继承父类中私有和非私有成员变量
创建的子类对象在堆内存中的空间被分为两块, 其中一块存储父类的所有成员变量(私有和非私有)
03 方法继承 – 子类可以继承父类中虚方法
java 为每个类创建一个对应的虚方法表, 每个类的虚方法表中包含来自父类的虚方法表中的方法
- 虚方法表中的方法:
- 非 private
- 非 static
- 非 final
虚方法表 – 子类调用一个方法, 若是虚方法, 就去虚方法表中找, 提高效率. 此外, 若子类调用的方法不是虚方法, 子类会现在自己类中查找, 若没有, 则会一层一层向上查找
示例内存图
02 继承中对象访问特点
01 继承中成员变量访问特点
就近原则: 谁离我近, 我访问谁
访问顺序:
- 局部变量
- 成员变量 – 使用 this 关键字
- 父类成员变量 – 使用 super 关键字
- 都没有, 则逐级向上查找
示例图
02 继承中成员方法访问特点
同样遵循就近原则
- this: 从本类开始查找
- super: 从父类开始查找
01 方法的重写
当父类的方法不能满足子类的需求时, 子类需要对父类方法进行重写
重写的本质: 使用子类中的方法替换从父类中继承的虚方法表中的方法
方法重写的对象: 具有继承关系的不同类之间的相同方法
方法的重载只与方法名和参数列表有关
02 书写格式
子类和父类中方法具有一摸一样的声明, 可以使用 @override
注解
03 方法重写的注意事项
- 重写方法的名称与形参列表必须与父类中的一致
- 子类重写父类的方法时, 访问权限必须大于等于父类 (public > protected > 没有权限修饰符 > private )
- 子类重写父类方法时, 返回值类型必须小于等于父类
- 建议: 重写的方法尽量和父类保持一致
- 只有被添加到虚方法表中的方法才能被重写
- 私有方法 private 不能被重写
- 静态放啊 static 不能被重写
03 继承中构造方法访问特点
- 构造方法不能被子类继承
- 子类中所有构造方法默认先访问父类中的无参构造(初始化父类数据), 再执行自己
- 子类构造方法的第一行语句默认
super();
, 不写也存在, 且必须在第一行
03 多态
创建的实例对象可以有多种形态, 使我们的代码更具灵活性 适用性
多态应用场景: 使用父类形参接收不同子类实参
多态: 同类型的对象, 表现出不同的形态
- 表现形式:
父类类型 对象名称 = 子类对象;
- 前提:
- 有继承关系
- 父类引用指向子类对象
Fu f = new Zi();
- 有方法重写 – 不同子类具有不同的行为
01 多态调用成员的特点
- 变量调用: 编译看左边, 运行也看左边
- 编译看左边: javac 编译代码的时候, 会看左边的父类中有没有这个变量, 如果有, 编译成功, 如果没有, 编译失败
- 运行看左边: java 运行代码的时候, 实际获取的就是左边父类中成员变量的值,
- 即使用多态创建对象, 访问变量时, 编译和运行都只在父类中查找,
- 方法调用: 编译看左边, 运行看右边
- 编译看左边: javac 编译代码时, 会看左边的父类中有没有这个方法, 如果有, 编译成功, 如果没有, 编译失败
- 运行看右边: java 运行代码时, 实际上运行的是子类中的方法
- 理解
Animal a = new Dog();
- 现在用变量 a 调用变量和方法, 而 a 是父类类型的, 所以会从父类中寻找变量和方法
- 所以, 对于成员变量调用, 若父类中没有, 子类中有, 则编译失败, 若父类子类中都有, 则使用父类中的成员变量, 即成员变量的调用看父类
- 所以, 对于成员方法调用, 如果子类对方法进行了重写, 那么虚方法表中是会把父类的方法进行覆盖的(子类的方法不应该是在子类的虚方法表中吗, 如果在父类虚方法表中查找调用方法, 不应该还是父类的方法吗? 路过的看官大佬, 若对多态调用有更加详细的理解, 请在评论区留言, 感之不尽!!!)
java 中, 先加载父类字节码文件, 再加载子类字节码文件 (Object类字节码文件先加载)
所以, Animal a = new Dog();
执行内存图如下
- 当执行改行代码时, 先加载父类Animal字节码文件, 再加载子类Dog字节码文件, 子类继承父类虚方法, 且覆盖重写show()方法
- 然后, 等式左边在栈内存的main方法内存中开辟一篇空间存储变量a
- 等式右边在堆内存中开辟一片空间存储对象Dog, 且该空间中一部分存储父类变量, 一部分存储子类变量
- 最后将堆内存中内存地址 001 赋值给栈内存中变量 a 空间中
执行代码sout(a.name);
调用变量 - 编译看左边: 编译时, 查看堆内存中对象空间中存储父类变量的那部分空间中有没有变量 name
- 运行看左边: 运行时, 查找堆内存中对象空间存储父类变量的那部分空间中变量 name 的值
- 结果:
动物
执行代码a.show();
- 编译看左边: 编译时, 在父类方法中查找有没有对应方法
- 运行看右边: 运行时, 去子类方法找
多态调用方法, 编译时 – 看父类
多态调用方法, 运行时 – 看子类
多态的优势:
- 在多态形式下, 右边对象可以实现解耦合, 便于扩展和维护
- 方法形参使用父类对象, 接受所有子类, 体现多态的扩展性和便利性
多态的劣势:
- 不能调用子类的特有方法. 因为, 父类中没有子类特有方法, 编译不通过
+ 解决方案: 向下转型
public static void main(String[] args) {
Fu fu = new Zi();
if(fu instanceof Zi){
Zi zi = (Zi)fu;
}
// jdk14新特性 若fu为Zi类, 则将其转换为子类zi, 并返回true
// 若fu不为Zi类, 则不转换, 并返回false
if(fu instanceof Zi zi){
//pass
}
}
}
小结
面向对象进阶
01 static – 共享
- static 表示静态, 是 java 中的一个修饰符, 可以修饰成员方法和成员变量, 称之为静态变量和静态方法
- 静态变量与静态方法属于类, 为所有实例化对象共享
- static 修饰的对象特点:
- 不属于对象, 属于类
- 被该类所有对象共享
- 随着类的加载而加载, 即优先于对象的加载, 即优先于对象的创建和存在
- 静态方法多用在测试类和工具类中
- javabean 类中很少使用
- 调用方式
- 类名调用
- 对象名调用
- static–note
- 静态方法只能访问静态变量和静态方法
- 非静态方法可以静态变量和静态方法, 也可以访问非静态的成员变量和非静态成员方法
- 静态方法中没有 this 关键字
01 static 静态对象(成员变量与成员方法)内存图
- 静态变量随着类的加载(类的字节码加载进内存)而加载, 优先于对象出现
- 静态变量的存储空间在堆内存中单独开辟一片空间
将 “啊玮老师” 赋值给堆内存中静态存储区的变量 “teachername”
类的实例化对象指向类的静态区
静态区的资源所有对象共享
02 小结
02 认识main方法
main方法中args数组传值处
01
02
03
public static void main(String[] args) {
System.out.println(args.length);
for (int i = 0; i < args.length; i++) {
System.out.println(args[i]); // 输出 111
}
}
03 包
包: 管理与不同的java类
包名规则: 公司域名反写 + 包的作用, 全部小写
全类名 (全限定名) = 包名+类名
可以这样写哦
还是这样写吧
使用其他类的规则:
- 使用同一个包中的类, 不需要导包
- 使用
java.lang
包中的类, 不需要导包 - 其他情况都需要导包
- 如果使用两个包中的同名类, 需要使用全类名
04 final 关键字
方法: 当该方法表示一种规则, 不希望被改变时, 使用 final 修饰
类: final class 类名{}
如 String 类 public final class String ...
String里面的方法你都不要动哦, final 修饰的类也不可被继承
变量: 一次赋值后不可改变, 赋值后变为常量
final int a;
a = 10;
常量
- 命名规则
- 全部大写, 单词之间用下划线隔开
- note
- final 修饰的变量是基本数据类型, 那么变量存储的数据值不能改变
- final 修饰的变量是引用数据类型, 那么变量存储的地址值不能改变, 但地址指向的对象内部数据值可以改变 比如对象名.setName(“111”);, 修改对象名的name属性吗但对象名存储的地址不可改变
note
- 字符串不可变, 就是有使用 final 修饰
05 权限修饰符
- 权限修饰符: 用来控制一个成员能够被访问的范围
- 成员: 变量, 方法, 构造方法, 类
权限修饰符作用范围
note: 实际开发中, 一般只用 public private
- 成员变量私有
- 方法一般使用public, 若方法中的代码是其他方法的共性代码, 这个方法一般也定义为私有
06 代码块
- 局部代码块
- 构造代码块
- 静态代码块
01 局部代码块 – 淘汰
- 写在方法里面的, 用大括号包住, 生命周期是一个大括号
- 作用: 提前结束变量的生命周期
02 构造代码块 – 淘汰
理解:
- 写在成员位置的代码块
- 作用: 可以把多个构造方法中重复的代码抽取出来
- 执行时机: 在创建本类对象时, 优先于构造方法执行
示例代码:
public class Student {
//构造代码块:
//1.写在成员位置的代码块
//2.作用:可以把多个构造方法中重复的代码抽取出来
//3.执行时机:我们在创建本类对象的时候会先执行构造代码块再执行构造方法
//渐渐的淘汰了
{
System.out.println("开始创建对象了");
} // 这就是构造代码块
public Student() {
System.out.println("空参构造");
}
public Student(String name, int age) {
System.out.println("有参构造");
this.name = name;
this.age = age;
}
03 静态代码块
格式: static{代码块}
特点: 随着类的加载而加载, 自动触发, 且执行一次, 即当第一次使用该类时执行, 即使第二次创建该类对象, 也不会执行
使用场景: 在类加载的时候, 做一些数据初始化的时候使用
07 abstract
抽象方法所在的类就是抽象类, 不能确定具体的方法体的将方法定义为抽象方法, 没有方法体, 所属类称为抽象类, 子类必须重写父类中的抽象方法
定义格式:
-
public abstract 返回值类型 方法名(参数列表);
重写时, 去掉abstract -
public abstract class 类名{}
特点:
- 抽象类不能实例化
- 抽象类中不一定有抽象方法, 有抽象方法的类一定是抽象类
- 抽象类可以有构造方法 – 创建子类对象时, 为父类中的公共数据初始化
- 抽象类的子类
- 必须重写父类所有抽象方法
- 或者子类也定义为抽象类
08 interface
- 接口: 一系列规则, 规范的方法的接口
- 抽象: 一类事物共性的抽取
01 接口的定义和使用
- 接口使用关键字 interface 定义
public interface 接口名{}
- 接口也不能实例化, 因为它里面的方法也有 抽象方法, 供子类实现
- 接口与类是实现的关系
public class 类名 implements 接口名{}
- 接口的子类(实现类)
- 要么重写接口中所有抽象方法
- 要么是抽象类
- 接口之间不能 implements, 可以 extends
- 接口也具有多态的特性, 与类的多态一样
note:
- 类实现接口, 可以是多实现
public class 类名 implements 接口名1, 接口名2{}
- 实现类可以在继承一个类的同时实现接口
public class 类名 extends 父类 implements 接口名1, 接口名2{}
02 接口中成员的特点
- 成员变量
- 只能是常量
- 默认修饰符:
public static final
- final: 一次赋值, 不可修改
- static: 方便调用 – 接口名.变量名
- public: 可以在任意处访问
- 构造方法
- 没有 – 不需要给实现类赋值
- 成员方法
- 只能是抽象方法 – jdk7以前
- 默认修饰符
public abstract
03 接口和类之间的关系
- 类和类 – 单继承, 可多层继承
- 类和接口 – 单实现 / 多实现(重写所有接口的抽象方法) / 边继承 边实现
- 若实现多个接口时, 出现重名方法怎么办??? : 重写一次就可, 表示将多个接口的重名方法都实现了
- 接口和接口 – 继承关系, 可以单继承也可多继承, 需要注意的是: 实现类需要实现所有相关接口的抽象方法
04 接口扩展
01 jdk8开始接口中新增的方法
- jdk8: 接口中可以定义有方法体的方法(默认 静态)
- jdk9: 接口中可以定义私有方法
jdk7的接口, 问题来了, 当接口新添加一个抽象方法时, 它的实现类就得立即重写该抽象方法, 造成不便
怎么办? – 在接口中可以添加有方法体的方法
- 允许在接口中定义默认方法, 使用关键字 default 修饰
- 接口中默认方法定义格式
public default 返回值类型 方法名(参数列表){}
- 默认方法不强制重写, 重写时, 去掉 default 关键字
- public 可以省略, default 不能省略
- 如果实现了多个接口, 多个接口中存在同名的默认方法, 子类就必须对该方法进行重写
- 允许在接口中定义静态方法, 使用关键字 static 修饰
- 接口中静态方法定义格式:
public static 返回值类型 方法名(参数列表){}
- 静态方法只能通过接口名调用, 不能通过实现类名或者对象名调用
- public 可以省略 static 不可省略
- 静态方法不能重写 (重写: 子类覆盖继承自父类虚方法表中的方法, 使用关键字 static 修饰的方法不在虚方法表中)
又有问题了 当接口中出现重复代码时, 这些代码只在接口内部使用, 不希望外部使用, 因此 jdk9 新增了私有方法
接口中私有方法的定义格式:
-
private 返回值类型 方法名(参数列表){}
– 供 default 方法使用 -
private static 返回值类型 方法名(参数列表){}
– 供 static 方法使用
02 接口应用
03 3适配器设计模式
- 适配器设计模式: 解决接口与接口实现类之间的矛盾问题
- InterAdapter: 创建一个 InterAdapter 对接口做一个空实现, 这样就可以创建只实现接口中特定方法的实现类, 同时 InterAdapter 不需要创建实例化对象, 所以将 InterAdapter 定义为 abstract 抽象类
09 内部类
类的五大成员: 属性 方法 构造方法 代码块 内部类
内部类: 内部类表示的事物是外部类的一部分, 内部类单独出现没有意义.
- 在一个类的里面, 再定义一个类
public class Outer{ // 外部类
public class Inner{ // 内部类
}
}
public class Test{ // 外部其他类
public static void main(String[] args){
}
}
- 内部类的访问特点
- 内部类可以直接访问外部类的成员, 包括私有
- 外部类要访问内部类的成员, 必须创建对象
01 内部类分类
- 成员内部类 – 写在成员位置的, 属于外部类的成员
- 代码书写
- 写在成员位置
- 成员内部类可以被一些修饰符所修饰 private 默认 protected public static
- jdk16之后, 内部类才可以定义静态变量
- 内部类创建格式
- 方式一
在外部类中编写方法, 对外提供内部类的对象
public class Outer{
private class Inner{
}
public Inner getInstance(){
return new Inner();
}
}
// 其他类调用外部类方法创建内部类对象
public class Test {
public static void main(String[] args) {
Object o = new Outer().getInstance();
System.out.println(o); // demo.Outer$Inner@1b6d3586 $:表示内部类
}
}
- 方式二
直接创建 外部类名.内部类名 对象名 = new 外部类对象().new 内部类对象();
- 成员内部类获取外部类对象属性和方法
public class Outer {
private int a = 10;
private int b = 10;
class Inner{
private int a = 20;
public void show(){
int a = 30;
System.out.println(b); // 10
System.out.println(a); // 30
System.out.println(this.a); // 20
System.out.println(Outer.this.a); // 1o
}
}
public Inner getInstance(){
return new Inner();
}
}
内存图分析 – 内部类访问局部变量、本类成员变量、外部类成员变量
说明:
- 类中的方法都有一个隐藏参数
本类名 this
, 即 this 指代当前调用该方法的对象 - 内部类中有一个隐藏成员变量
Outer this;
此处this指向外部类, 用于内部类访问外部类 - 静态内部类
使用 static 修饰的成员内部类就是静态内部类
静态内部类只能访问外部类中的静态变量和静态方法, 如果想要访问非静态的需要创建对象
创建格式:外部类名.内部类名 对象名 = new 外部类名.内部类名()
外部类名.内部类名.静态方法名()
调用静态内部类方法:
- 调用内部类非静态方法: 先创建内部类对象 再调用
- 调用内部类静态方法:使用类名调用也可以
外部类名.内部类名.方法名();
- 局部内部类
- 将内部类定义在方法里面就叫局部内部类, 类似于方法里面的局部变量
- 外界无法直接使用, 需要在方法内部创建局部内部类对象并使用
- 该类可以直接访问外部类的成员,也可以访问方法内部的局部变量
- 匿名内部类
匿名内部类本质上就是隐藏了名字的内部类,可以写在成员位置或局部位置 – 继承父类或实现子类
格式:
new 类名或接口名(){
重写方法
}; // 不要忘了匿名内部类的大括号后面接分号
// 格式说明
1. 继承和实现:继承和实现类名或接口名表示的抽象类或接口
2. 方法重写:对继承和实现的对象需要重写的方法进行重写
3. 创建对象:new 关键字表示创建对象
# 整体理解: 大括号表示的是子类或是实现类(匿名内部类),类名或接口名表示的是继承的父类或实现的子类,new 关键字表示的是创建大括号表示的类的对象(匿名内部类对象)。整体就是一个类的子类对象或接口的实现类对象
**应用场景:**如调用方法的实参传入一个匿名内部类, 或赋值给父类或接口
tip:在命令行窗口使用 javap
反编译java.class文件,查看字节码文件表示的内容
10 浅克隆和深克隆
- 浅克隆: 不管对象内部的属性是基本数据类型还是引用数据类型, 都完全拷贝过来, 即 拷贝的是变量中存储的值, 基本数据类型拷贝数据值, 引用数据类型拷贝地址值
- 深克隆: 基本数据类型不变, 字符串复用(常量池中的字符串的地址复用), 引用数据类型重新开辟一片空间使用
11 转义字符
转义字符的说明
/* 转义字符
* \ 是转义字符 改变其后面那个字符的原本含义
* 比如: " -- 双引号, 在 java 中有特殊的含义: 表示一个字符串的开始或结尾
* 当需要打印一个双引号时, 需要用到转义字符, 将其含义由独特表示转换为一个普通的双引号
* System.out.println(" \" ");
* 又比如: java 中路径的表示 \\ 使用双斜杠
* 表示: 前一个斜杠是转义字符, 将后一个斜杠转义字符转义为普通斜杠
* 如果, 只写一个斜杠, 表示的是 该转义字符 \ 将其后面的字符进行了转义
*/
11 正则表达式 Regex
note: 帮助文档 查 pattern
类
正则表达式是一个字符串
可以校验字符串是否满足一定的规则, 并用来校验数据格式的合法性
作用:
- 校验字符串是否满足规则
- 在一段文本中查找满足要求的内容
01 校验字符串
代码示例
// 只匹配一个 -- 即只对单个字符进行判断
System.out.println("a".matches("[abc]")); // true
System.out.println("b".matches("[abc]")); // true
System.out.println("c".matches("[abc]")); // true
// 判断两个字符的字符串, 可以写两个正则规则
System.out.println("ab".matches("[abc]")); // false
System.out.println("ab".matches("[abc][abc]")); // true
/* 转义字符
* \ 是转义字符 改变其后面那个字符的原本含义
* 比如: " -- 双引号, 在 java 中有特殊的含义: 表示一个字符串的开始或结尾
* 当需要打印一个双引号时, 需要用到转义字符, 将其含义由独特表示转换为一个普通的双引号
* System.out.println(" \" ");
* 又比如: java 中路径的表示 \\ 使用双斜杠
* 表示: 前一个斜杠是转义字符, 将后一个斜杠转义字符转义为普通斜杠
* 如果, 只写一个斜杠, 表示的是 该转义字符 \ 将其后面的字符进行了转义
*/
System.out.println("---------------------------------");
// 使用预定义字符判断单个字符
System.out.println("我".matches(".")); // true . 匹配任意一个字符
System.out.println("我是".matches("..")); // true 多个字符, 每一个字符都需要一个判断
System.out.println("我是".matches(".")); // false
System.out.println("我是".matches("...")); // false
System.out.println("---------------------------------");
// 判断数字
System.out.println("0".matches("\\d")); // true
System.out.println("12".matches("\\d\\d")); // true
System.out.println("123".matches("\\d\\d")); // false
System.out.println("12".matches("\\d")); // false
匹配多个字符
代码示例
// 一次匹配多个字符
// 必须是字符 下划线 字母 至少6为
System.out.println("123456a_".matches("\\w{6,}")); // true
System.out.println("12a_".matches("\\w{6,}")); // false
// 必须是数字 字母 且必须只能是4位
System.out.println("1234".matches("[0-9a-zA-Z]{4}")); //true
System.out.println("1a2Aq".matches("[0-9A-Za-z]{4}")); // false
System.out.println("23df".matches("[\\w&&[^_]]{4}")); // true
System.out.println("23d_".matches("[\\w&&[^_]]{4}")); // false
02 爬虫
介绍两个类:
-
Pattern:
表示正则表达式 -
Matcher:
文本匹配器 – 即按照正则表达式的规则去读取字符串, 从头开始读取, 在大串中寻找符合匹配规则的字串
代码示例
public static void main(String[] args) {
//new MainJFrame();
String text = "java是世界上最好的语言, java8真好用, java11真不错!!!";
// 使用 Pattern 和 Matcher 对 text 查询 javaXX 字段
Pattern pattern = Pattern.compile("java\\d{0,2}");
Matcher m = pattern.matcher(text);
// 调用 Matcher 对象的 find 方法, 判断 text 中是否有满足 pattern 的字段
// find: 返回 boolean, 并且保留查询到的字段的开始索引和结束索引+1 -- (start, end+1)
//m.find();
// 调用 Matcher 对象的 group 方法
// group: 底层根据 find 记录的 (start, end+1) 截取 text, 即将查询到的字段以字符串的形式返回
// 同理subString(start,end+1); 返回索引 (start, end) 的字段
//String s = m.group();
// 再次调用 find, 从当前位置继续向后查找, 查询第二个符合条件的字段, 所以使用循环
boolean flag = true;
while (flag){
flag = m.find();
if (!flag){
break;
}
String str = m.group();
System.out.println(str);
}
}
03 正则表达式在字符串方法中的使用
这里直接使用字符串的对应方法, 即可按照参数中正则表达式的规则, 操作字符串
说明
-
str1.matches(regex1);
– 判断 str1 是否符合 regex1 -
str1.replaceAll(regex1, str2);
– 将 str1 中符合 regex1 的部分替换位 str2 -
str1.split(regex1);
– 按 str1 中符合 regex1 的部分, 将 str1 进行切割
注意事项
- 正则表达式一个匹配范围对应一个字符, 多个字符即字符串的匹配需要使用多个匹配范围
- 当同时匹配不同字符串时, 可以使用
|
符号. 如:(正则表达式1) | (正则表达式2)
- 贪婪爬取
"a+"
– 多少个a都要 - 非贪婪爬取
"a+?"
– 一长串a时, 只要第一个就好 - 贪婪爬取和非贪婪爬取主要适用于
+ *
01 非捕获分组–代码示例
public static void demo(){
String text = "Java8 java11 jAva11 java是世界上最好的语言, java,有很多版本, 比如java9, java10, java8真好用, java11真不错!!!";
// 正则表达式联系
// 1. 查询 带有版本号的java, 但是只要8和11
String regex1 = "(?i)java(?=8|11)";
String regex2 = "(?i)java(?:8|11)";
String regex3 = "(?i)java(8|11)"; // 和 regex2 功能一样
String regex4 = "(?i)java(?!8|11)"; //
/*
?表示占位符, 表示java
=表示将java与后面的字符拼接
即 regex1 表示 java8 | java11
但是:使用Matcher对象的group方法获取字符串时, 只获取?表示的数据 java
(?i) 忽略大小写
: 获取时带上后面的数据 8 | 11
! 匹配除了java8 java11的其他javaXX, 并只获取?表示的数据
*/
Pattern p =Pattern.compile(regex4);
Matcher m = p.matcher(text);
while(m.find()){
System.out.println(m.group());
}
}
上面代码主要说的就是非捕获分组
代码示例
02 分组 ()
分组
捕获分组
public static void demo2(){
// 捕获分组
String regex1 = "(.)(.+)\\1";
// \\组号 : 表示把第 X 组的内容拿出来, 再用一次, 即当前组的匹配规则和 X 组的匹配规则一样
}
代码示例
总结
API
application programming interface
应用程序编程接口
API帮助文档
如何使用帮助文档
01 字符串
01 String
string概述
java.lang.String 类代表字符串, java程序中所有字符串文字(如:“abc”)都为此类对象
字符串的内容不会发生改变, 它的对象创建后值不可改变
String name = "zhangsan";
name = "lisi"
需要强调的是, 上述代码中, 并没有发生字符串的改变, 只是创建了两个不同的字符串, 改变了name的指向
创建String方式
- 根据字符数组或字节数组创建字符串, 字节数组转成对应的ascii值转换成字符串
java内存之字符串常量池
- 只有使用直接赋值的方式创建的字符串才在字符串常量池中, jdk7之后, 字符串常量池位于堆内存中
- 当使用双引号直接赋值时, 系统会检查该字符串在常量池中是否存在,
- 存在: 复用–将其地址赋值给对应字符串变量
- 不存在: 创建新的
通过构造方法创建字符串, 不能复用, 浪费内存空间
01 Stirng 方法
01 比较
- “==” 比较的是什么 – 变量空间中存储的是数据
- 基本数据类型比较的是自己空间中的数据值
- 引用数据类型比较的是自己空间中的地址值
比较两个字符串的值
直接使用双等号比较符比较的是地址值, 所以一般使用string方法equals比较两个字符串表示的值是否一样
public static void main(String[] args) {
String a = "abc";
String b = "abc";
String c = new String("abc");
System.out.println(a==b); // true
System.out.println(a==c); // false 此处比较的是地址值
System.out.println(a.equals(c)); // true 调用方法比较的是字符串值
}
根据索引返回字符
02 StringBuilder
String Builder: StringBuilder是一个内容可变的容器, 效率高于String
- 作用: 提高字符串的操作效率
01 构造方法
02 常用方法
note: 在用 StringBuilder 操作完之后, 需要调用其 sb.toString() 方法, 将Stringbuilder对象变量重新转换为String对象
03 StringJoiner
- StringJoiner 和 StringBuilder 一样, 也可以看成一个容器, 创建后里面的内容也是可变的
- 作用: 提高字符串操作效率, 而且代码编写简洁
- jdk8
01 构造方法
指定间隔符号以及开始和结束符号
02 成员方法
04 字符串底层原理
字符串存储的内存原理
- 直接赋值会复用字符串常量池中的 – 字符串常量池位于堆中
- new 出来的不会复用 – 单独在堆中开辟一片空间, 用于存放 new 出来的字符串对象
== 号的比较
- 基本数据类型比较的是变量表示空间中的数据值
- 引用数据类型比较的是变量表示空间中的地址值
字符串拼接底层原理
- 如果没有变量参与, 都是字符串直接相加, 会在编译javac之后, 将其直接转换为多个字符串想加的最终结果, 会复用字符串池中的字符串, 提高效率
- 如果有变量参与, 会创建新的字符串, 浪费内存
StringBuilder
- 原理: 所有要拼接的内容都会往 StringBuilder 中放, 不会创建很多无用的中间字符串, 节约内存
02 ArrayList–集合
定义: 可以自动扩容即**长度可变(底层自动实现)**的数组
note:
- 存储类型: 集合只能存储引用类型, 基本类型需要使用包装类
基本数据类型对应的包装类 – jdk1.5之后自动转换
01 ArrayList
-
<E>
: 表示泛型, 用于限制ArrayList存储的数据类型 - 输出打印arraylist对象名, 为
[元素1, 元素2]
01 创建 – 空参构造
public static void main(String[] args) {
ArrayList arr1 = new ArrayList(); // 无泛型限制的集合, 可以add任意类型的数据
arr1.add(1);
arr1.add("abc");
System.out.println(arr1.get(0));
System.out.println(arr1.get(1));
System.out.println(arr1); // [1, abc] 输出打印结果
ArrayList<Integer> arr2 = new ArrayList<>(); // 有泛型限制的集合, 只能添加泛型限制的数据类型的数据
arr2.add(1);
// arr2.add("abc"); error!!! 只能添加 Integer 类型的数据
}
02 成员方法
03 System
提供一些与系统相关的方法
04 Runtime
Runtime 由java给出, 在如下代码中表明, 程序中创建的 Runtime 对象 只能是同一个
运行cmd命令
如: shutdown -s
默认一分钟后关机
shutdown -s -t
指定多少秒后关机
shutdown -a
取消关机操作
shutdown -r
关机并重启
05 Object
- Object 是Java的顶级父类
- 没有成员变量 – 没有一个属性是所有类共有的, 所以只有空参构造, 所以类的构造方法默认调用的是父类的空参构造
object的clone方法是浅克隆
equals() 在object中是比较地址值, 即直接比较两个变量
06 Objects
Objects 是一个工具类, 提供了一些操作对象方法供我们使用
07 BigInteger
BigInteger: 就是获得一个大的整数
01 构造方法
note:
- 使用静态方法获取 BigInteger 对象, 内部有优化
- 表示的范围较小, 只能表示 long 的取值范围之内
- 在内部对常用的数字进行了优化( -16 ~ 16)
- 提前把 -16 ~ 16 的 BigInteger 对象先创建好, 如果多次获取不会重新创建新的对象
- 对象一旦创建, 内部记录的值就不能发生改变
- 即, 只要进行计算都会产生一个新的 BigInteger 对象
02 成员方法
BigInteger 是对象, 不能直接进行四则运算BigInteger 底层存储数据原理
目前内存扛不住的
08 BigDecimal
作用: 解决小数计算不精确的问题
- 用于小数的精确计算
- 用来表示很大的小数
小数计算问题如下
System.out.println(0.01); // 0.01
System.out.println(0.09); // 0.09
System.out.println(0.01 + 0.09); // 0.09999999999999999
double d1 = 0.01;
double d2 = 0.09;
System.out.println(d1); // 0.01
System.out.println(d2 ); // 0.09
System.out.println(d1 + d2 ); // 0.09999999999999999
BigDecimal bd1 = new BigDecimal(0.01);
BigDecimal bd2 = new BigDecimal(0.09);
System.out.println(bd1); // 0.01000000000000000020816681711721685132943093776702880859375
System.out.println(bd2); // 0.0899999999999999966693309261245303787291049957275390625
BigDecimal bd3 = new BigDecimal("0.01");
BigDecimal bd4 = new BigDecimal("0.09");
System.out.println(bd3); // 0.01
System.out.println(bd4); // 0.09
System.out.println(bd3.add(bd4)); // 0.10
构造方法
-
public BigDecimal(double val)
– 该方法有不可预知的不确定性 不建议使用 -
public BigDecimal(String val)
– 该方法没有第一个构造放啊的不确定性 -
public static BigDecimal valueOf(double val)
– 通过静态方法获取 BigDecimal 对象
note:
- 如果表示的数字不大, 没超过double的取值范围, 建议使用静态方法
- 如果表示的数字过大, 建议使用第二个构造方法
- 使用静态方法时, 如果传递的是 0-10之间的整数, 多次调用, 返回的是具有相同地址值的同一个对象
成员方法
09 Date
01 jdk7前时间相关类
-
Date
– 时间 -
SimpleDateFormat
– 格式化时间 -
Calendar
– 日历
代码示例
Date
SimpleDateFormat
作用:
- 格式化: 把时间变成我们喜欢的格式
- 解析: 把字符串表示的时间变成 date 对象
格式化: 调用 SimpleDateFormat
的 format
方法, 将一个 Date
对象转换称我们需要的时间格式, 返回一个字符串
解析: 调用 SimpleDateFormat
的 parse
方法, 将一个表示时间的字符串转换成 Date
对象, 以供后续操作
代码示例
Calendar
note
- 获取日历对象
-
Calendar.getInstance();
– 底层原理: 根据系统的不同时区来获取不同的日历对象, 会把时间中的纪元 年 月 日 时 分 秒 星期 等都放到一个数组中, 默认表示当前时间 - 在日历对象中, 月 0-11 星期日是一个星期的第一天
add
正数对某个字段加 负数对某个字段减
02 jdk8新增时间相关类
01 ZoneID
02 Instant 时间戳
获取一个不带时区的时间, 即标准时区的时间
代码示例
03 ZoneDateTime
代码示例
04 DateTimeFormatter
DateTimeFormatter
用于时间的格式化和解析
05 LocalDate LocalTime LocalDateTime
-
Localdate
: 年 月 日 -
LocalTime
: 时 分 秒 LocalDateTime
: 年 月 日 时 分 秒
转换
代码示例
LocalTime
LocalDateTime
06 Duration Period ChronoUnit
10 包装类
包装类: 基本数据类型对应的包装类
以 Integer 为例
这是jdk5以前, 现在直接用就好
note
- radix 表示进制
- 静态方法对-127 ~ 128 (128- -127 + 1 =256) 256个Integer对象建立了一个数组, 获取对象时, 直接从数组中获取
- jdk5之后, 可以自动拆箱和装箱
Integer 成员方法
note:
- 使用
Integer
类型转换方法parseInt()
时, 字符串必须是数字
2 除了Character
类型, 其他类型都有parseXxx()
方法
补充
- 键盘录入 使用
nextLine() 返回一个字符串
private static void testKeyboard() {
// 键盘录入
Scanner sc = new Scanner(System.in);
System.out.println("请输入: ");
//System.out.println(sc.next());
/*
使用获取特定类型的方法的局限性:
1. 遇到空格 制表符等停止, 无法接收后面的内容
2. 建议使用 nextLine(), 遇到回车停止
*/
System.out.println(sc.nextLine());
}
10 Arrays
插件
- ptg:一键生成标准javabean
- Translation: 翻译插件, 如将源码中的英文注释翻译成中文
案例 – 拼图小游戏
事件:
- 事件就是可以被组件识别的操作
- 当你对组件干了某件事情之后,就会执行对应的代码
- 三属性
- 事件源 – 按钮 图片
- 事件 – 某些操作,鼠标点击等
- 绑定监听
美化界面
查看完整图片 A 键
一键通关 W 键
- 整个游戏界面添加键盘事件监听
- 编写相应的逻辑代码
判断胜利 使用win二维数组
3. 胜利后显示胜利图片
4. 胜利后取消键盘监听事件
计数功能
- 使用 JLabel 类(管理图片和文字)