- Java 核心语法
- 一、 面向对象
- 1、 简介
- 2、 类
- 3、 构造器
- 4、 this
- 5、 封装
- 6、 标准 JavaBean
- 二、 常用 API(1)
- 1、 简介
- 2、 String
- 2.1 构造字符串
- 2.2 内存原理
- 2.3 操作功能
- 2.3.1 内容比较
- 2.3.2 其他常用功能
- 2.3.3 开发验证码案例
- 3、 ArrayList
- 3.1 快速入门
- 3.2 泛型编程
- 3.3 常用功能
- 3.4 案例
- 3.4.1 遍历删除
- 3.4.2 元素搜索
- 三、 面向对象高阶
- 1、 static
- 1.1 用法
- 1.2 内存原理
- 1.3 基本用法
- 1.4 应用
- 1.4.1 开发工具类
- 1.4.2 代码块
- 1.4.3 单例设计模式
- 1.4.3.1 饿汉单例设计模式
- 1.4.3.2 懒汉单例模式
- 2、 继承
- 2.1 概述
- 2.2 特点
- 2.3 方法重写
- 2.5 子类构造器
- 3、 包
- 4、 final 关键字
- 4.1 作用
- 4.2 语法
- 5、 枚举
- 5.1 枚举概述
- 5.2 枚举使用
- 6、 抽象类
- 6.1 概念
- 6.2 使用场景
- 6.3 模板方法模式
- 5、 多态
- 5.1 简介
- 5.2 强制类型转换
- 1、 static
- 四、 接口
- 1、 概念
- 2、 基本使用
- 3、 多继承
- 4、 新增方法
- 4.1 默认方法
- 4.2 静态方法
- 4.3 私有方法
- 5、 注意事项
- 五、 内部类
- 1、 概念
- 2、 内部类分类
- 2.1 静态内部类
- 2.2 成员内部类
- 2.3 局部内部类
- 2.4 匿名内部类
- 六、 常用 API(2)
- 1、 Object
- 2、 Objects
- 3、 StringBuilder
- 4、 Math
- 5、 System
- 6、 BigDecimal
- 7、日期类
- 7.1 Date
- 7.2 SimpleDateFormat
- 7.3 Calendar
- 8、java.time
- 9、 包装类
- 10、 正则表达式
- 11、 Arrays
- 七、 常见算法
- 1、 选择排序
- 2、 二分法查找
- 八、 匿名函数
- 一、 面向对象
面向对象(oop)是一种编程思想
- 面向对象的三大基本特征:
- 封装
- 继承
- 多态
- 面向对象的编程思想就是把事物看作一个整体,从事物的特征(属性)和行为(方法)两个方面进行描述。
- 面向对象的过程就是找对象、建立对象、使用对象、维护对象的关系的过程
面向对象的三大核心特性:
- 可重用性:代码重复使用,减少代码量,提高开发效率。面向对象的三大基本特征(继承、封装和多态)都围绕这个核心。
- 可扩展性:指新的功能可以很容易地加入到系统中来,便于软件的修改。
- 客观理性:能够将功能与数据结合,方便管理。
抽象
所谓的抽象,就是把同一类事物中共有的特征(属性)和行为(功能、方法)进行抽取,归纳,总结。
抽象的过程其实就是面向对象编程的核心思想
2、 类类是共同特征的描述;对象是真实存在的具体实例
类包括:
- 成员变量:描述类或者对象的属性信息
- 成员方法:描述类或者对象的行为信息
- 构造器:初始化一个类的对象返回
- 代码块
- 内部类
语法:
设计类
public class 类名 {}
得到类对象
类名 对象名 = new 类名();
使用对象
访问属性
对象名.成员变量;
访问行为
对象名.方法名();
注意:
-
类名首字母建议大写,不能使用关键字,必须是合法的标识符
-
一个 Java 文件中可以定义多个类,但只能一个类是 public 修饰,而且 public 修饰的类名必须成为代码的文件名。实际开发中建议一个代码文件只定义一个类
-
成员变量的完整定义格式是
-
修饰符 数据类型 变量名称 = 初始化值;
一般无需初始化值
-
3、 构造器
作用:
- 定义在类中的,可以用于初始化一个类的对象,并返回对象的地址
语法:
定义语法
修饰符 类名(形参列表) {}
调用构造器的格式
Car c = new Car("h", 3.1);
构造器分类和作用
- 无参构造器:
- 初始化对象时,成员变量的数据均采用默认值(默认存在的)
- 有参构造器:
- 在初始化对象的时候,同时可以接收参数为对象进行赋值
public class Car {
String name; // 名称
double price; // 价格
public Car(String n, double p) {
this.name = n;
this.price = p;
}
}
4、 this
可以出现在构造器、方法中
代表当前对象的地址
public class Goods {
String name; // 名称
double price; // 价格
public Goods(String n, double p) {
this.name = n;
this.price = p;
}
public void print() {
System.out.println(this.name); // 代表当前对象的地址
}
}
5、 封装
作用:
- 告诉我们,如何正确设计对象的属性和方法
如何封装呢?
- 一般建议对成员变量使用 private (私有,隐藏)关键字修饰(private 修饰的成员只能在当前类中访问)
- 为每个成员变量提供配套 public 修饰的 getter、setter 方法暴露去取值和赋值
package demo001;
public class Goods {
String name; // 名称
private int id; // 私有变量只能在类内访问
public Goods() {
}
public Goods(String name, int id) {
this.name = name;
this.id = id;
}
public void setArg(int id, String name) {
this.name = name;
// 使用 if 判断,防止有问题的数据进入
if (id > 0) {
this.id = id;
}
else {
System.out.println("id应大于0");
this.id = 0;
}
}
public void getArg() {
// 获取属性
System.out.println("id:" + this.id + "\nname:" + this.name);
}
}
6、 标准 JavaBean
其也可以称为实体类,其对象可以用于在程序中封装数据
必须满足需求:
- 成员变量使用 private 修饰
- 提供成员变量对应的设置和获取值的方法
- 需要提供无参构造器
数组中存储的元素并不是对象本身,而是对象的地址
二、 常用 API(1) 1、 简介API 是应用程序接口
- Java 写好的技术(功能代码),我们可以直接调用
- Oracle 也为 Java 提供的这些功能代码提供了相应的API文档
下载API文档:【https://docs.oracle.com/en/java/javase/18/docs/api/index.html】
2、 String简单介绍:
- String 类定义的变量可以用于存储字符串,同时 String 类提供了很多操作字符串的功能,我们可以直接使用
- java.lang.String 类代表字符串,String 类定义的变量可以用于指向字符串对象,然后操作该字符串
语法:
// 直接构造
String name = "李华";
name += "你好"; // 进行了重新赋值
// 通过构造器构造
// 直接构造
String a = new String("abc");
// 通过字符数组创建
char[] a1 = {'a'. 'b'};
String a2 = new String(a1);
// 通过字节数组创建
byte[] b1 = {23, 45, 67};
String b2 = new String(b1);
特点:
- String 其实常被称为不可变字符串类型,它的对象在创建后不能被更改
双引号创建的字符串对象,在字符串常量池中存储同一个
通过 new 构造器创建的字符串对象,在堆内存中分开存储
2.3 操作功能 2.3.1 内容比较语法:
a.equals(A); // 比较值的大小,忽略地址不同
a.equalsIgnoreCase(A); // 忽略大小写比较值的大小
String Name = "李华", Pwd = "123";
Scanner sc = new Scanner(System.in);
System.out.print("登录名称:\t");
String name = sc.next();
System.out.print("密码:\t\t");
String pwd = sc.next();
// 判断用户输入的内容是否等于系统存储的内容
/*
if (Name == name && Pwd == pwd) { // 由于内存地址不一样,比较失败
System.out.println("登录成功!");
} else {
System.out.println("登录失败!");
}
*/
if (Name.equals(name) && Pwd.equals(pwd)) { // 比较内容
System.out.println("登录成功!");
} else {
System.out.println("登录失败!");
}
// 忽略大小写比较
String a = "a";
String A = "A";
System.out.println(a.equalsIgnoreCase(A)); // 忽略大小写比较
2.3.2 其他常用功能
String name = "我爱你中国!";
System.out.println(name.length()); // 获取字符串的长度
System.out.println(name.charAt(1)); // 获取索引为1的值
// 遍历字符串
for (int i = 0; i < name.length(); i++) {
System.out.println(name.charAt(i));
}
char[] name_arr = name.toCharArray(); // 将字符串转换成字符数组
System.out.println(name.substring(0, 2)); // 顾左不顾右
name = name.replace("我", "大家"); // 替换字符串
System.out.println(name);
System.out.println(name.contains("中国")); // 判断字符串中是否包含中国
System.out.println(name.startsWith("大家")); // 判断字符串是否以大家开始
String[] name_new = name.split("爱"); // 以爱为界限分割字符串
System.out.println("[" + name_new[0] + ", " + name_new[1] + "]");
2.3.3 开发验证码案例
public static String verify(int n) {
Random r = new Random();
String temp = "abcdefjhigklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
String code = "";
for (int i = 0; i < n; i++) {
// 多少位验证码
int index = r.nextInt(temp.length());
code += temp.charAt(index);
}
return code;
}
3、 ArrayList
简介:
- ArrayList 代表的是集合类,集合是一种容器,与数组类似,不同的是集合的大小不固定的
- 通过创建 ArrayList 的对象表示得到一个集合容器,同时 ArrayList 提供了比数组更好用,更丰富的API(功能)
- 集合大小不固定,启动后可以动态变化,类型也可以选择不固定
- 里面的元素可以重复
创建集合对象
import java.util.ArrayList;
ArrayList list = new ArrayList();
list.add("java"); // 有返回值,添加成功为真
list.add(1);
list.add(1, "a"); // 给指定位置添加元素
System.out.println(list);
3.2 泛型编程
泛型概述:
-
ArrayList<E>:其实就是一个泛型类,可以在编译阶段约束集合只能操作某种数据类型(E)
ArrayList<Integer> list = new ArrayList<Integer>(); // 整型
注意:
- 后面的<Integer>可以不用添加
注意:
- 集合中只能存储引用数据类型,不支持基本数据类型
- 约束集合只能存储一种数据类型
ArrayList<Integer> list = new ArrayList<Integer>();
for (int i = 0; i < 10; i++) {
list.add(i); // 快速生成
}
System.out.println(list);
int get = list.get(3); // 得到索引位置的元素值
System.out.println(get);
System.out.println(list.size()); // 得到集合的元素个数
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i)); // 遍历集合
}
System.out.println(list.remove(0)); // 删除对应索引位置的元素值,并且返回删除的元素值
list.remove((Integer)2); // 删除对应的值,返回 bool 类型,当有重复元素时,删除最左边的
System.out.println(list);
list.set(0, 100); // 返回修改的值
System.out.println(list);
3.4 案例
3.4.1 遍历删除
ArrayList<Integer> l = new ArrayList<>();
Random r = new Random(); // 生成随机的数据
for (int i = 0; i < 10; i++) {
l.add((Integer) r.nextInt(100));
}
System.out.println(l);
/* 第一种
for (int i = 0; i < l.size(); i++) {
if (l.get(i) < 60) {
l.remove(i);
// 删除后,由于少了一个元素,i会后移,要使用i--避免i后移
i--;
}
}
System.out.println(l);*/
// 第二种
for (int i = l.size() - 1; i >= 0 ; i--) {
if (l.get(i) < 60) {
l.remove(i);
// 这个方法不用删除
}
}
System.out.println(l);
3.4.2 元素搜索
学生类:
package demo001;
public class Students {
// 学生类
private String m_Name;
private String m_Id;
private int m_Age;
public String getM_Name() {
return m_Name;
}
public String getM_Id() {
return m_Id;
}
public int getM_Age() {
return m_Age;
}
public void setM_Name(String m_Name) {
this.m_Name = m_Name;
}
public void setM_Id(String m_Id) {
this.m_Id = m_Id;
}
public void setM_Age(int m_Age) {
this.m_Age = m_Age;
}
public Students() {
} // 无参构造
public Students(String m_Name, String m_Id, int m_Age) {
this.m_Name = m_Name;
this.m_Id = m_Id;
this.m_Age = m_Age;
} // 有参构造
}
功能实现:
package demo001;
import java.util.ArrayList;
import java.util.Scanner;
public class demo1 {
public static void main(String[] args) {
ArrayList<Students> l = init();
// printInfo(l);
Scanner sc = new Scanner(System.in);
System.out.print("请输入要查找的学生学号:");
searchInfoById(l,sc.next());
}
private static ArrayList<Students> init() {
// 初始化功能,可以从数据库中存储
ArrayList<Students> l = new ArrayList<>();
l.add(new Students("李华", "001", 23));
l.add(new Students("李四", "002", 22));
l.add(new Students("王五", "003", 24));
l.add(new Students("李六", "004", 25));
return l;
}
private static void printInfo(ArrayList<Students> l) {
// 打印信息功能
for (int i = 0; i < l.size(); i++) {
System.out.println("----------------------------------");
System.out.println("名字:" + l.get(i).getM_Name());
System.out.println("学号:" + l.get(i).getM_Id());
System.out.println("年龄:" + l.get(i).getM_Age());
}
System.out.println("----------------------------------");
}
private static void searchInfoById(ArrayList<Students> l, String id) {
// 查找学生
boolean flag = false; // 默认没找到
for (int i = 0; i < l.size(); i++) {
if (id.equals(l.get(i).getM_Id())) {
flag = true;
System.out.println("名字:" + l.get(i).getM_Name());
System.out.println("学号:" + l.get(i).getM_Id());
System.out.println("年龄:" + l.get(i).getM_Age());
break;
}
}
if (!flag) {
System.out.println("没找到该信息");
}
}
}
三、 面向对象高阶
1、 static
1.1 用法
static 是静态的意思,可以修饰成员变量和成员方法
static 修饰成员变量表示该成员变量只在内存中只存储一份,可以被共享访问、修改
成员变量:
-
静态成员变量(有static修饰,属于类,内存中加载一次):常表示在线人数等需要被共享的信息,可以被共享访问
语法:
// 定义 public static int onlineNumber = 161; // 访问 类名.静态成员变量(推荐) 对象.静态成员变量(不推荐) User.onlineNumber; // 同一个类中,访问静态成员变量也可以不用加类名
-
实例成员变量(无static修饰,存于每个对象中):常表示姓名、年龄等属于每个对象的信息
语法:
// 定义 public int m_Age = 12; // 访问 对象.实例成员变量 User u = new User(); u.m_Age;
静态变量和类一起优先加载,加载完成后存入堆内存,其只有一份数据
1.3 基本用法成员方法分类:
- 静态成员方法(有static修饰,归属于类),建议使用类名访问,也可以使用对象访问
- 实例成员方法(无static修饰,归属于对象),只能用对象触发访问
package com.company;
public class Main {
public static void main(String[] args) {
// 静态成员方法的访问:在类内,直接使用 函数名 访问;在类外使用 类名.函数名 访问。不推荐使用对象访问静态方法
System.out.println(Student.getMax(10, 3));
}
}
class Student {
// 实例化成员变量:无static修饰,属于对象;实例化成员方法只能通过对象访问
private String name;
// 静态成员方法:有static修饰,归属于类,可以别共享访问,用类名或者对象名都可以访问
public static int getMax(int age1, int age2) {
return age1 > age2 ? age1 : age2;
}
}
1.4 应用 1.4.1 开发工具类使用场景:
- 表示对象自己的行为的,且方法中需要直接访问实例成员,则该方法必须声明成实例方法
- 如果该方法是以执行一个通用功能为目的,或者需要方便访问,则可以声明成静态方法
注意事项:
- 静态方法只能访问静态的成员,不可以直接访问实例成员
- 实例方法可以访问静态的成员,也可以访问实例成员
- 静态方法中是不可以出现this关键字的
工具类是什么?
- 类中都是一些静态方法,每个方法都是以完成一个共用的功能为目的,这个类用来给系统开发人员使用
由于工具类里面都是静态方法,直接使用类名即可访问。因此,工具类无需创建对象,建议将工具类的构造器进行私有
public class Tools {
private Tools() {
// 私有化构造函数,使得类无法实例化对象
}
}
好处:
- 内部都是一些静态方法,每个方法完成一个功能
- 一次编写,处处可用,提高代码的重用性
要求:
- 建议工具类的构造器私有化处理
- 工具类不需要实例化对象
如,制作一个数组转换工具
package com.company;
public class Main {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 9};
System.out.println(ArrTools.toString(arr));
}
}
class ArrTools {
// 私有化构造器
private ArrTools() {}
public static String toString(int[] arr) {
// 校验数组
if (arr == null) {
return null;
}
// 拼接内容
String ret = "[";
for (int i = 0; i < arr.length; i++) {
ret += (i != arr.length - 1 ? arr[i] + ", " : arr[i]); // 使用三目运算
}
return ret + "]";
}
}
1.4.2 代码块
概述:
- 代码块是类的5大成分之一(成员变量、构造器、方法、代码块、内部类),定义在类中方法外
- 在java类下,使用
{}
括起来的代码被称为代码块
代码块分为
- 静态代码块
- 格式:
static {}
- 特点:需要通过static关键字修饰,随着类的加载而加载,并且自动触发、只执行一次
- 使用场景:在类加载的时候做一些静态数据初始化的操作,一遍后续使用
- 格式:
- 构造代码块
- 格式:
{}
- 特点:每次创建对象,调用构造器执行时,都会执行该代码块中的代码,并且在构造器执行前执行
- 使用场景:初始化实例资源
- 格式:
什么是设计模式?
- 开发中经常遇到一些问题,一个问题通常由n中解法,但其中有一种解法是最优的,这个最优的解法被人总结出来了,称之为设计模式
- 设计模式有20多种,对应20多种软件开发中会遇到的问题
- 在使用类获取对象的时候,对象已经提前创建好了
设计步骤:
- 定义一个类,把构造器私有化
- 定义一个静态变量存储一个对象
package com.company;
public class SinglePattern {
// 把构造器私有化
private SinglePattern() {}
// 准备好对象,这个对象只能是一个
public static SinglePattern sp = new SinglePattern();
}
1.4.3.2 懒汉单例模式
- 在真正需要对象的时候,才去创建一个对象(延迟加载对象)
设计步骤:
- 定义一个类,把构造器私有
- 定义一个静态变量存储一个对象
- 提供一个方法返回一个单例对象
public class SinglePattern {
// 把构造器私有化
private SinglePattern() {}
// 准备好对象,这个对象只能是一个,注意要私有化单例对象
private static SinglePattern sp;
// 准备一个静态方法,返回一个单例对象
public static SinglePattern getSp() {
if (sp == null) { // 如果该对象是第一次构造
sp = new SinglePattern();
}
return sp;
}
}
2、 继承
2.1 概述
什么是继承?
-
java中提供了一个关键字extends,用这个关键字,我们可以让一个类和另一个类建立起父子关系
public class Student extends People {}
-
Student称为子类(派生类),People称为父类(基类或超类)
继承后子类的特点?
- 子类继承父类,子类可以得到父类的属性和行为,子类可以使用
- java中的子类更强大
继承设计规范:
- 子类具有的相同特征(共性属性、共性方法)放在父类中定义,子类独有的属性和行为应该定义在子类自己里面
继承特点:
- 子类可以继承父类的属性和行为,但是子类不能继承父类的构造器
- 子类可以继承父类的私有成员,只是不能直接访问
- 子类可以共享父类中的静态成员,但是不是继承
- java是单继承模式:一个类只能继承一个直接父类
- java不支持多继承,但是支持多层继承
- java中所有的类都是Object类的子类
访问特点:
-
在子类方法中访问成员(成员变量、成员方法)满足:就近原则
-
先子类局部范围找
-
然后子类成员范围查找
-
然后父类成员范围找,如果父类范围还没有则报错
-
this.name; // 访问子类中的的name变量 super.name; // 访问父类中的name变量 name; // 就近访问name变量
-
什么是方法重写?
- 在继承体系中,子类出现了和父类中一模一样的方法声明,我们就称子类这个方法是重写的方法
方法重写应用场景:
- 当子类需要父类的功能,但父类的该功能不完全满足自己的需求时,子类可以重写父类中的方法
package com.company;
public class Main {
public static void main(String[] args) {
B b = new B();
b.a();
}
}
class A{
public void a() {
System.out.println("s");
}
}
class B extends A{
// 重写校验注解,加上之后,这个方法必须是正确重写的,这样更安全
@Override
public void a() {
super.a();
System.out.println("a更新了");
}
}
2.5 子类构造器
@Overide
重写注解:
- 其是放在重写的方法上,作为重写是否正确的校验注解
- 加上该注解后如果重写错误,编译阶段会出现错误提示
- 建议重写方法都加上重写注解,代码安全,优雅!
重写注解注意事项:
- 重写方法的名称和形参列表必须与被重写方法的名称和常数列表一致
- 私有方法不能重写
- 子类重写父类方法时,访问权限必须大于或等于父类
- 子类不能重写父类的静态方法,如果重写会报错
子类中所有的构造器默认都会先访问父类中无参的构造器,再执行自己
package com.company;
public class Main {
public static void main(String[] args) {
B b = new B();
B c = new B("name");
}
}
class A{
public A() {
System.out.println("父类构造器");
}
}
class B extends A{
public B() {
System.out.println("子类无参构造器");
}
public B(String name) {
System.out.println("子类有参构造器");
}
}
为什么?
- 子类在初始化的时候,有可能会使用到父类中的数据,如果父类没有完成初始化,子类无法使用父类的数据
- 子类初始化之前,一定要调用父类构造器,先完成父类数据空间的初始化
那么子类构造器如何访问父类的有参构造器呢?
package com.company;
public class Main {
public static void main(String[] args) {
B c = new B("name", 1);
}
}
class A{
private String name;
private int age;
public A(String name, int age) {
this.name = name;
this.age = age;
System.out.println("父类有参调用");
}
}
class B extends A{
public B(String name, int age) {
// 调用父类的有参构造器,初始化继承自父类的数据
super(name, age);
System.out.println("子类有参调用");
}
}
3、 包如果父类中没有无参构造器,只有有参构造器,会出现什么现象呢?
- 会报错,因为子类默认是调用父类无参构造器
解决方法:
- 子类构造器中可以通过书写
super(参数)
,手动调用父类的有参构造器
什么是包?
- 包是用来分门别类的管理各种不同类的,类似于文件夹,建包利于程序的管理和维护
- 建包的语法格式:
package ...;
,建议全部英文小写,且具备意义 - 建包语句必须在第一行,一般IDEA工具会帮助创建
导包
- 相同包下的类可以直接访问,不同包下的类必须导包,才可以使用!导包格式:
import 包名.类名;
访问权限问题
4、 final 关键字 4.1 作用- final 关键字是最终的意思,可以修饰(类、方法、变量)
- 修饰类:表明该类是最终类,不能被继承
- 修饰方法:表明该方法是最终方法,不能被重写
- 修饰变量:表示该变量第一次赋值后,不能再次赋值(即为常量)
package com.company;
public class Main {
public static void main(String[] args) {
final public static String name = "李华"; // 表明该变量为常量,无法再次赋值修改
}
}
class A {
public final void a() {
// 表明该方法无法重写
System.out.println("这是无法重写的函数");
}
}
final class B {} // 表明是最终类,无法被继承
注意事项:
- final修饰的变量是基本类型:那么变量存储的数据值不能发生改变
- final修饰的变量是引用类型:那么变量存储到额地址值不能发生改变,但是地址指向的对象内容是可以发生变化的
常量:
- 常量是使用了
public static final
修饰的成员变量,必须有初始化值,而且执行的过程中其值不能被改变 - 常量的作用和好处:可以用于做系统的配置信息,方便程序的维护,同时也能提高可读性
- 常量命名规范:英文单词全部大写,多个单词下划线连接
- 常量的执行原理:
- 在编译阶段会进行“宏替换”,把使用常量的地方全部替换成真实的字面量
- 这样做的好处是让使用常量的程序执行性能与直接使用字面量是一样的
5、 枚举 5.1 枚举概述
-
枚举是Java中的一种特殊类型
-
枚举的作用是为了做信息的标志和信息的分类
-
定义枚举类的格式:
修饰符 enum 枚举名称 { 第一行都是罗列枚举类实例的名称 }
枚举特征:
- 枚举类都是继承了枚举类型:
java.lang.Enum
- 枚举都是最终类,不可以被继承
- 枚举类的构造器都是私有的,枚举对外不能创建对象
- 枚举类的第一行默认都是罗列对象的名称的
枚举做信息标准和分类:
- 代码可读性好,入参约束严谨,代码优雅,是最好的信息分类技术,建议使用
package com.company;
public class Main {
public static void main(String[] args) {
test(A.SPRING);
}
public static void test(A a) { // 只能接收枚举类型的变量作为传入的信号,增加代码的可读性
switch(a) {
case SPRING:
System.out.println("spring");
break;
case SUMMER:
System.out.println("summer");
break;
case AUTUMN:
System.out.println("autumn");
break;
default:
System.out.println("winter");
break;
}
}
}
enum A {
// 枚举的第一行必须罗列枚举类的对象名称,建议全部使用大写
SPRING, SUMMER, AUTUMN, WINTER;
}
6、 抽象类
6.1 概念
-
在java中abstract是抽象的意思,可以修饰类、成员方法
-
abstract修饰类,这个类就是抽象类;修饰方法,这个方法就是抽象方法
public abstract class A { // 抽象类,用abstract修饰 public abstract void run(); // 只写声明,不需要写实现,像C++里面的纯虚函数一样 }
注意事项:
- 抽象方法只有方法签名,不能声明方法体
- 一个类中如果定义了抽象方法,这个类必须声明成抽象类,否则报错
抽象类一般可以理解为不完整的设计图,一般作为父类,让子类继承
这个就像C++里面的抽象类的使用方法一样
注意事项:
- 类有的成员(成员变量、方法、构造器),抽象类都具有
- 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
- 一个类继承了重选in类必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类
- 不能用abstract修饰变量、代码块、构造器
- 得到了抽象方法,失去了创建对象的能力
使用场景说明:当系统中出现同一个功能多处在开发,而该功能中大部分代码是一样的,只有其中部分可能不同的时候
实现步骤:
- 把功能定义成一个所谓的模板方法,放在抽象类中,模板方法中只定义通用且能确定的代码
- 模板方法中不能决定的功能定义成抽象方法让具体子类去实现
作用:
- 提高了代码的复用性
- 模板方法已经定义了通用结构,模板方法不能确定的部分定义成抽象方法,交给子类实现。因此,使用者只需要关心自己需要实现的功能即可
什么是多态?
- 同类型对象,执行同一个行为,会表现出不同的行为特征
多态的常见形式
父类类型 对象名称 = new 子类构造器;
接口 对象名称 = new 实现类构造器;
多态中成员访问特点
- 方法调用:编译看左边,运行看右边
- 变量调用:编译看左边,运行也看左边(多态侧重行为多态)
多态的前提
- 有继承/实现关系;有父类引用指向子类对象;有方法重写
优势:
-
在多态模式下,右边对象可以实现解耦合,便于扩展和维护
Animal a = new Dog(); a.run(); // 后续业务行为岁对象而变,后续代码无需更改
-
定义方法的时候,使用父类作为参数,该方法就可以接收这父类的一切子对象,体现出多态的扩展性与便利
自动类型转换:子类对象赋值给父类类型的变量指向
强制类型转换:
- 此时必须进行强制类型转换:
子类 对象变量 = (子类)父类类型变量
- 作用:可以解决多态下的劣势,可以实现调用子类独有的功能
- 注意:如果转换类型后到的类型和对象真实类型不是同一种类型,那么在转换的时候就会出现
ClassCastException
Java 建议强转转换前使用instanceof
判断当前对象的真实类型,再进行强制转换
接口的定义与特点:
-
接口的定义格式如下:
public interface 接口名 { // 常量 // 抽象方法 }
-
什么是接口?
- 接口也是一种语法规范
- 规范默认要是公开的,所以代码层面上可以不用写
public static final / public abstrat
接口的用法:
- 接口是用来被类实现的,实现接口的类称为实现类。实现类可以理解成所谓的子类
- 接口可以被类单实现,也可以被类多实现
修饰符 class 实现类 implements 接口1, 接口2 ... {} // 实现的关键字implements
如,我定义了一个接口为Main接口
package com.company;
public class achieveInter implements Main{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public achieveInter(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println("跑步");
}
@Override
public void competiton() {
System.out.println("比赛");
}
}
3、 多继承注意事项:
- 一个类实现接口,必须重写全部接口的全部抽象方法,否则这个类需要定义成抽象类
- 类与类的关系,单继承
- 类和接口的关系,多实现
- 接口与接口的关系:多继承,一个接口可以同时继承多个接口
语法:
public interface Main extends People, Hello{} // 多继承
4、 新增方法
4.1 默认方法
- 类似之前写的普通实例方法:必须使用default修饰
- 默认会用public修饰,需要用接口的实现类的对象来调用
default void run() {
System.out.println("hello world");
}
4.2 静态方法
- 默认会public修饰,必须static修饰
- 注意:接口的静态方法必须用本身的接口名来调用
static void inAddr() {
System.out.println("hello world");
}
4.3 私有方法
- 就是私有的实例方法,必须使用private修饰,从 jdk1.9 才开始有
- 只能在本类中被其他的默认方法或者私有方法访问
private void go() {
System.out.println("出发");
}
新增的方法我们在开放中很少使用,通常是Java源码中涉及到。现阶段需要理解、识别语法,明白调用关系即可。
5、 注意事项- 接口不能创建对象
- 一个类实现多个接口,多个接口中有同样的静态方法不冲突
- 一个类继承了父类,同时又实现了接口,父类中和接口中有同名方法,默认使用父类的
- 一个类实现了多个接口,多个接口中存在同名默认方法,不冲突,这个类重写该方法即可
- 一个接口继承多个接口,是没有问题的;如果多个接口中存在规则冲突则不能多继承
内部类
-
类内部就是定义在一个类里面的类,里面的类可以理解成(寄生),外部类可以理解成(宿主)
public class People { // 内部类 public class Heart { // 代码块 } }
内部类的使用场景、作用
- 当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构可以悬着使用内部类设计
- 内部类通常可以方便访问外部类的成员,包括私有成员
- 内部类提供更好的封装性,内部类本身就可以用
private protexted
等修饰,封装性可以做更多的控制
什么是静态内部类?
- 有static修饰,属于外部类本身
- 它的特点和使用与普通类是完全一样的,类有的成分它都有,只是位置在别人里面而已
// 创建
public class Outer {
// 静态成员内部类
public static class Inner {}
}
// 静态内部类创建对象的格式
Outer.Inner in = new Outer.Inner();
2.2 成员内部类注意事项:
- 内部静态类中可以访问外部类的静态成员
- 但是不能访问外部类的实例成员
什么是成员内部类?
- 无static修饰,属于外部类的对象
- 成员内部类中可以定义静态成员
// 创建
public class Outer {
// 成员内部类
public class Inner {}
}
// 成员内部类创建对象的格式
Outer.Inner in = new Outer().new Inner();
2.3 局部内部类可以直接访问外部类的静态成员,实例方法中可以直接访问外部类的实例成员
注意:
- 在成员内部类中访问所在外部类对象,格式:
外部类名.this
- 局部内部类放在方法、代码块、构造器等执行体中
- 局部内部类的类文件名为:
外部类$N内部类.class
public class Test {
public static void main(String[] args) {
class Cat {
// 此为局部类,一般不使用,有违面向对象的思想
}
}
}
2.4 匿名内部类
- 本质上是一个没有名字的局部内部类,定义在方法中、代码块中等
- 作用:方便创建子类对象,最终目的为了简化代码编写
格式:
new 类|抽象类名|接口名 {
重写方法;
};
// 如:
Employee a = new Employee() {
// 这个类可以是抽象类,相当于具体化抽象类,但是要实现抽象类里面所有的抽象函数
public void work() {
System.out.println("the worker is working");
}
};
a.work(); // 调用类内部的函数
特点:
- 匿名内部类是一个没有名字的内部类
- 匿名内部类写出来就会产生一个匿名内部类的对象
- 匿名内部类的对象类型相当于是当前new的那个类型的子类类型
Object类的作用:
- 一个类要么默认继承了Object类,要么间接继承了Object类,Object类是Java中的祖宗类
- Object类的方法是一切子类都可以直接使用的,所以我们要学习Object类的方法
常用方法
toString
问题引出:
- 开发中直接输出对象,默认输出对象的地址其实是毫无意义的
- 开发中输出对象变量,更多的时候是希望看到对象的内容数据而不是对象的地址信息
toString
存在的意义:
- 父类
toString()
方法存在的意义就是为了被子类重写,以便返回对象内部的信息,而不是地址信息
package com.company;
public class Test {
private int age;
private String name;
@Override
public String toString() {
// 通过重写方法来返回对象的数据
return "Test{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
equals
问题思考:
- 直接比较两个对象的首地址是否相同完全可以使用“==”替代
equals
equals
存在的意义:
- 父类
equals
方法存在的意义就是为了被子类重写,以便子类自己来定制比较规则
package com.company;
import java.util.Objects;
public class Test {
private int age;
private String name;
@Override
public boolean equals(Object o) {
if (this == o) return true; // 如果地址相同
if (o == null || getClass() != o.getClass()) return false; // 防止空指针异常
Test test = (Test) o; // 强转类型
return age == test.age && Objects.equals(name, test.name); // 父类的方法更安全,防止出现空指针异常,使得程序崩溃
}
}
2、 Objects
概述:
- Objects类与Object还是继承关系
Objects的常见方法:
对象进行内容比较的时候建议使用Objects提供的equals方法,比较结果一样,但是其更安全
3、 StringBuilder概述:
- StringBuilder是一个可变的字符串类,我们可以把它看做是一个对象容器
- 作用:提高字符串的操作效率,如拼接、修改等
StringBuilder构造器:
因为对字符串进行修改后,其返回对象本身,故其支持链式编程
package com.company;
public class Main {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
String name = "lihua";
sb.append(name).append("今年").append("23岁了"); // 支持链式编程
System.out.println(sb.reverse()); // 输出反转后的字符串
System.out.println(sb.length()); // 获取字符串的长度
String ret = sb.toString(); // 将拼接后的结果转换为字符串类型
}
}
4、 Math注意:
- StringBuilder只是拼接字符串的手段,效率好
- 最终目的还是要恢复成String类型
- 定义字符串使用String,拼接、修改等操作字符串使用StringBuilder
概述:
- 包含执行基本数字运算的方法,Math类没有提供公开的构造器
- 如何使用类中的成员呢?看类的成员是否都是静态的,如果是,通过类名就可以直接调用
Math中的常用方法:【https://www.runoob.com/java/java-number.html】
5、 Systemsystem类概述:
- System的功能是通用的,都是直接用类名调用即可,所以System不能被实例化
System类常用方法:【http://c.biancheng.net/view/904.html】
6、 BigDecimal作用:
- 用于解决浮点型运算精度失真的问题
使用步骤:
-
创建对象BigDecimal封装浮点型数据(最好的方式是调用方法)
public static BigDecimal valueOf(double val); // 包装浮点数成为一个对象 BigDecimal dc = BigDecimal.valueOf(0.1); // 创建对象
-
调用API
BigDecimal常用API:【https://blog.csdn.net/qq_39182939/article/details/119299240】
类概述:
- 在程序中继承需要处理时间,获取时间、时间判断、运算等
Date构造器:
public Date(); // 创建一个Date对象,代表的是系统当前此刻日期时间
public Date(long time); // 把时间毫秒值转换为Date日期对象
常用方法:
public long getTime(); // 获取时间对象的毫秒值
public void setTime(long time); // 设置日期对象的时间为当前时间毫秒值对应的时间
7.2 SimpleDateFormat
作用:
-
可以对Date对象或时间毫秒值格式化成我们喜欢的时间格式
-
也可以把字符串的时间格式解析成日期对象
格式化的时间形式的常用模式对应关系:
y 年 2021-11-11 13:27:06 ----> yyyy-MM-dd HH:mm:ss
M 月 2021年11月11日 13:27:06 ----> yyyy年MM月dd日 HH:mm:ss
d 日
H 时
m 分
s 秒
使用语法:
// 日期对象
Date d = new Date();
System.out.println(d);
// 格式化日期对象(指定最终格式的形式)
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
// 开始格式化日期对象成为喜欢的字符串格式,参数也可以传入毫秒数
String f_date = sdf.format(d);
System.out.println(f_date);
// -----------*******----------------
// 解析字符串事件成为字符串日期对象,形式必须与被解析时间的形式完全一样,否则运行时解析报错
String date_str = "2022年04月28日 09:20:47";
try {
Date now_date = sdf.parse(date_str);
System.out.println(now_date);
} catch (ParseException e) {
e.printStackTrace();
}
7.3 Calendar
概述:
- Calendar代表了系统此刻日期对应的日历对象
- Calendar是一个抽象类,不能直接创建对象
常用方法:
8、java.time日历是可变日期对象,一旦修改后其对象本身表示的时间将产生变化
参数里面的field常数为日历类里面的常量
常量 描述 Calendar.YEAR 年份 Calendar.MONTH 月份 Calendar.DATE 日期 Calendar.DAY_OF_MONTH 日期,和上面的字段意义完全相同 Calendar.HOUR 12小时制的小时 Calendar.HOUR_OF_DAY 24小时制的小时 Calendar.MINUTE 分钟 Calendar.SECOND 秒 Calendar.DAY_OF_WEEK 星期几
LocalDate:不包含具体时间的日期
LocalTime:不包含日期和时间
LocalDateTime:包含了日期及时间
Instance:代表的时间戳
DateTimeFormatter:用于做时间的格式化和解析的
Duration:用于计算两个时间间隔
Period:用于计算两个日期间隔
由于方法较多,使用时可以在网上或官方文档查找详细的语法
官方文档:【https://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html】
9、 包装类包装类:
- 其实就是8中基本维护数据类型对应的引用类型
byte Byte
short Short
int Integer
long Long
char Char
float Float
double Double
boolean Boolean
为什么要提供包装类?
- java为了实现一切皆对象,为8中基本类型提供了对应的引用类型
- 后面的集合和泛型其实也只能支持包装类型,不支持基本数据类型
性质:
- 自动装箱:
- 基本类型的数据和变量可以直接赋值给包装类型的变量
- 自动拆箱:
- 包装类型的变量可以直接赋值给基本数据类型的变量
包装类特有功能:
-
包装类的变量的默认值可以是null,容错率更高
-
可以把基本数据类型的数据转化成字符串类型
Integer a = 1; // 调用Integer.toString(数据) System.out.println(Integer.toString(a)); // 调用toString() 方法得到字符串结果 System.out.println(a.toString());
-
可以把字符串类型的数值转换成真实的数据类型
String in = "1"; // 将字符串数据转换成整型数据 System.out.println(Integer.parseInt(in)); System.out.println(Integer.valueOf(in)); // 将字符串型数据转换成浮点型数据 System.out.println(Double.parseDouble(in)); System.out.println(Double.valueOf(in));
官方文档里面有较多正则匹配规则:【https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/util/regex/Pattern.html】
使用示例:
System.out.println("a".matches("[abc]]")); // 匹配是否为abc其中的一个
System.out.println("a".matches("[^abc]]")); // 匹配是否不能为abc其中的一个
System.out.println("a".matches("\\d")); // 匹配是否为数字
System.out.println("a".matches("\\w")); // 匹配是否为字母
System.out.println("abcsdfsa".matches("\\w{6,20}")); // 匹配是否为6到20位的字母
定义匹配规则使用正则:
// 定义匹配规则
String reg = "\\d+";
// 把这个匹配规则编译成匹配对象
Pattern pattern = Pattern.compile(reg);
// 得到一个内容匹配器对象
Matcher matcher = pattern.matcher("hello");
// 开始匹配
while (matcher.find()) {
String ret = matcher.group();
System.out.println(ret);
}
11、 Arrays
Arrays类概述:
- 数组操作工具类,专门用于操作数组元素的
Arrays类的常用API:【http://c.biancheng.net/view/5885.html】
Integer[] p = {10, 20, 30, 40, 5};
// 返回数组内容
System.out.println(Arrays.toString(p));
// 排序
Arrays.sort(p);
System.out.println(Arrays.toString(p));
// 二分搜索技术,返回不存在元素的规律:- (应该插入的值的位置的索引 + 1)
// 注意:二分法查找要先将数组排好序
int index = Arrays.binarySearch(p, 10);
System.out.println(index);
// 降序排序
Arrays.sort(p, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1; // 如果o1大于o2,返回负整数
/*
如果`认为`左边数据大于右边数据,返回正整数
如果`认为`左边数据小于右边数据,返回负整数
如果`认为`左边数据等于右边数据,返回0
**/
}
});
System.out.println(Arrays.toString(p));
七、 常见算法 1、 选择排序注意Comparator自定义选择器,可以传入自定义的数据类型,自定义排序规则
选择排序的思想:
- 每轮选择当前位置,开始找出后面的较小值与该位置交换
选择排序的关键:
- 确定总共有需要选择几轮:数组的长度 - 1
- 控制每轮从以前位置为基准,与后面元素选择几次
实现代码:
// 定义一个数组
int[] p = {1, 20, 50, 2, 10, 30};
// 定义一个循环控制语句,注意,最后一个数字不需要判断,所以是len - 1,这里是降序排序
for (int i = 0; i < p.length - 1; i++) {
for (int j = i + 1; j < p.length; j++) {
// 内部循环需要判断最后一个数
if (p[i] < p[j]) {
int temp = p[i];
p[i] = p[j];
p[j] = temp;
}
}
}
// 输出数组
System.out.println(Arrays.toString(p));
2、 二分法查找
引入背景:
- 当在数据量特别大的时候,基本查找从前往后寻找的性能很差
二分法查找:
- 二分法查找性能好,二分查找的前提是必须是排好序的数据
二分法查找正常的检索条件应该是开始位置 小于等于 结束位置
代码实现:
public static int sort_arr(int[] p, int data) {
int left = 0, right = p.length - 1;
// 开始循环,折半查询
while (left <= right) {
// 取中间索引
int middleIndex = (left + right) / 2;
// 判断中间位置的元素和要找的元素的大小情况
if (data > p[middleIndex]) {
// 如果数据大于中间的数据,左位置更新为中间位置
left = middleIndex;
}
else if (data < p[middleIndex]) {
// 如果数据小于中间数据,右位置更新为中间位置
right = middleIndex;
}
else {
// 如果数据与中间数据相等,返回索引值
return middleIndex;
}
}
// 如果没有找到数据,返回-1
return -1;
}
八、 匿名函数
Lambda概述:
- Lambda表达式是JDK8开始后的一种新语法形式
语法:
(匿名内部类被重方法的形参列表) -> {
被重写方法的方法体代码;
}
注:->是一种语法形式,没有实际含义
使用示例:
package com.company;
public class Main {
public static void main(String[] args){
Swimming s1 = () -> {
System.out.println("你在游泳"); // 相当于重写了函数式接口里面的函数,括号里面填写函数的参数
};
go(s1); // 调用函数
}
private static void go(Swimming s1) {
System.out.println("走去游泳呀!");
s1.swim();
System.out.println("游泳完了呀!");
}
}
@FunctionalInterface // 一旦加上这个注释就必须是函数式接口,里面只能有一个抽象方法
interface Swimming{
void swim();
}
注意:
- Lambda表达式只能简化函数式接口的匿名内部类的写法形式
什么是函数式接口?
- 首先必须是接口、其次接口中有且仅有一个抽象方法的形式
- 通常我们会在接口上加一个
@FunctionalInterface
注解,标记该接口必须是满足函数式接口
Lambda表达式的省略写法:
- 参数类型可省略不写
- 如果只有一个参数,参数类型可以省略,同时()也可以省略
- 如果Lambda表达式的方法体代码只有一行代码,可以省略大括号不写,同时要省略分号
- 如果Lambda表达式的方法体代码只有一行代码,可以省略大括号不写。此时,如果这行代码是return语句,必须省略不写,同时也必须省略 ';' 不写