skip to content
Logo Logo ZhenXI Blog

Java SE

/ 81 min read

Java SE学习


1 static关键字

1.1 static定义

  • static可以修饰成员变量和成员方法。
  • static修饰成员变量表示该成员变量只在内存中存储一份,可以被共享访问,修改。

1.2 static的访问方法

  • 类名.静态成员变量(常用)
  • 对象.静态成员变量(不常用)
static访问方法演示

1.3 实例成员变量访问方式

  • 对象.实例成员变量
package com.liu.day01;
public class User {
//创建静态成员变量
public static int onlineNumber = 100;
//实例成员变量,属于每个对象,必须要用对象名访问
private String name;
private int age;
public static void main(String[] args) {
//1.通过 类名.静态成员变量 来访问静态成员变量
System.out.println(User.onlineNumber);
//2.通过 对象.静态成员变量 来访问静态成员变量
User user = new User();
System.out.println(++user.onlineNumber);
//同一个类中的静态成员变量的访问可以省略类名
user.onlineNumber++;
System.out.println(onlineNumber);
//实例成员变量访问
user.name = "zhenxi";
user.age = 23;
System.out.println(user.age);
System.out.println(user.name);
}
}

1.4 两种成员变量在什么情况定义

  • 静态成员变量:对象需要共享的信息
  • 实例成员变量:属于每个对象,且每个对象的信息不同时

1.5 static成员变量的内存机制

  • 静态成员变量会在创建方法区时就在堆内存中产生。
static的内存机制

1.6 static成员方法访问方式

  • 与静态成员变量访问方法一致:类.静态成员方法
  • 实例成员方法访问方式:对象.实例成员方法
package com.liu.day01;
public class Student {
private String studentName;
private String studentId;
//静态成员方法:属于该类
public static int getMax(int age1,int age2){
return age1>age2 ? age1:age2;
}
//实例方法:属于对象
public void study(){
System.out.println(studentName+"在好好学习。");
}
public static void main(String[] args) {
//通过类直接访问静态成员方法
System.out.println(getMax(10, 20));
//通过对象来访问实例方法
Student student = new Student();
student.studentName="zhenxi";
student.study();
Student student1 = new Student();
student1.studentName="努力";
student1.study();
}
}

1.7 static成员方法内存机制

static成员方法内存机制

1.8 static访问注意事项

  • 静态方法只能访问静态成员,不可以直接访问实例成员。
访问事宜
  • 实例方法可以访问静态成员,也可以访问实例成员。
image-20220719182445718
  • 静态方法中是不可以出现this关键字的。
不能出现this关键字

1.9 static应用知识:工具类

  • 工具类的内部都是一些静态的方法,每个静态方法完成一个功能。
  • 一次编写,处处可用,提高代码的重用性,避免重复造轮子。
  • 工具类的构造器要进行私有化处理。

例如1:编写一个创造随机验证码的工具类LoginUtils

package com.liu.day01;
import java.util.Random;
//工具类
public class LoginUtils {
//工具类无需创建对象,故把工具类的构造器进行私有化。
private LoginUtils(){}
//创建一个随机验证码
public static String createVerifyCode(int n){
String code = "";
String data = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
Random random = new Random();
for (int i = 0; i < n; i++) {
int index = random.nextInt(data.length());
code += data.charAt(index);
}
return code;
}
}

当其它类需要生成验证码时,直接使用:类.静态成员方法,即可调用。

image-20220719224100800

例如2:编写定义一个数组工具类

package com.liu.day01;
public class ArraysUtils {
//首先,私有化构造器,使外部不能创建对象
private ArraysUtils(){}
//返回整数数组中的内容方法
public static String toString(int[] array){
//首先对传进来的数组进行校验
if (array == null){
return null;
}
//对数组中的内容进行拼接
String result = "[";
for (int i = 0; i < array.length; i++) {
result += (i == array.length-1?array[i]:array[i]+",");
}
result += "]";
return result;
}
//统计平均值 只考虑浮点型数组
public static float getAerage(float[] array){
//首先对传进来的数组进行校验
if (array == null){
return 0;
}
float aerAge = 0;
float max=array[0];//最大值
int maxIndex=0;//最大值索引
float min=array[0];//最小值
int minIndex=0;//最小值索引
//通过循环找出最大值最小值
for (int i = 0; i < array.length; i++) {
if (max<array[i]){
max = array[i];
maxIndex = i;
}
if (min>array[i]){
min = array[i];
minIndex = i;
}
}
//定义一个新数组,将除去最大值最小值的数据放入新数组中
float[] newArray= new float[array.length-2];
int j = 0;
for (int i= 0; i < array.length; i++) {
if (!(i == maxIndex || i == minIndex)){
newArray[j]=array[i];
j++;
}
}
//求得新数组中的总数
float sum=0;
for (int i = 0; i < newArray.length; i++) {
sum += newArray[i];
}
//获得平均值
aerAge=sum/array.length;
return aerAge;
}
}

数组工具类的调用测试:

image-20220719232124419

1.10 static应用知识:代码块

  • 代码块是类的5大成分。
  • 格式:static{}。
  • 特点:随着类的加载而加载,并且自动触发只执行一次。
  • 使用场景:在类加载的时候做一些静态数据初始化的操作。
package com.liu.day01;
import java.util.ArrayList;
public class StaticDemo2 {
/*
* 静态资源*/
public static String schoolName;
/*
* 实例资源*/
private String name;
//静态代码块 可以用于初始化静态设置
static {
System.out.println("---静态代码块被执行---");
schoolName = "zhenxi";
}
//实例代码块 不常用了解即可
{
System.out.println("---实例代码块执行---");
name="努力";
}
//无参构造
public StaticDemo2(){
System.out.println("---无参构造执行---");
}
public static void main(String[] args) {
System.out.println("---main方法执行---");
System.out.println(schoolName);
StaticDemo2 staticDemo2 = new StaticDemo2();
System.out.println(staticDemo2.name);
}
}

运行结果:

---静态代码块被执行---
---main方法执行---
zhenxi
---实例代码块执行---
---无参构造执行---
努力
Process finished with exit code 0

例二:静态代码块初始化牌组

package com.liu.day01;
import java.util.ArrayList;
public class StaticDemo3 {
public static ArrayList<String> cards = new ArrayList<>();
static {
String[] sizes = {"2","3","4","5","6","7","8","9","10","J","Q","K","A"};
String[] colors = {"","","","桃花"};
for (int i = 0; i < sizes.length; i++) {
for (int j = 0; j < colors.length; j++) {
String card = sizes[i] + colors[j];
cards.add(card);
}
}
cards.add("小🃏");
cards.add("大🃏");
}
public static void main(String[] args) {
System.out.println(cards);
}
}

1.11 单例设计模式

饿汉单例模式

  • 定义一个类,把构造器私有。
  • 定义一个静态变量存储一个对象。
package com.liu.day01;
public class SingleInstance {
//第一步:私有化构造器
private SingleInstance(){}
//第二步:创建一个静态对象
public static SingleInstance singleInstance = new SingleInstance();
}
package com.liu.day01;
public class Test2 {
public static void main(String[] args) {
//通过类. 来获得对象
SingleInstance singleInstance = SingleInstance.singleInstance;
}
}

懒汉单例设计模式:在真正需要该对象时,才会去创建一个对象。

  • 定义一个类,私有化构造器。
  • 定义一个静态成员变量存储一个对象。
  • 提供一个返回单例对象的方法。
package com.liu.day01;
public class SingleInstance2 {
//1.私有化构造器
private SingleInstance2(){}
//2.创建一个静态变量 这里私有化是为了防止通过类名.静态成员变量获得instance
private static SingleInstance2 instance;
//3.提供一个方法,对外返回单例对象
public static SingleInstance2 getInstance(){
//首先判断instance是否第一次创建,若是则创建对象,若不是则直接返回对象。
if (instance == null){
instance =new SingleInstance2();
}
return instance;
}
}
package com.liu.day01;
public class Test2 {
public static void main(String[] args) {
//通过类.静态成员方法 来获得对象
SingleInstance2 instance = SingleInstance2.getInstance();
}
}

2 继承

2.1 继承的定义和好处

  • 继承就是Java允许我们用extends关键字,让一个类和另一个类建立一种父子关系。

  • 优点:提高代码的复用性,减少了代码冗余,增强类的功能扩展性。

  • 子类继承父类,子类可以得到父类的属性和行为,子类比父类更强大

例如:父类People

package com.liu.day01.extends_test;
public class People {
//子类都有的属性
private String name;
private int age;
//子类都有的方法
public void lookCourse(){
System.out.println("查看课表");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}

子类:Teacher

package com.liu.day01.extends_test;
public class Teacher extends People{
//子类特有的属性
private String department;
//子类特有的方法
public void releaseIssues(){
System.out.println("发布问题~");
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
}

子类:Student

package com.liu.day01.extends_test;
public class Student extends People{
//子类特有的属性
private String className;
//子类特有的方法
public void learning(){
System.out.println("听课");
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
}

测试:

package com.liu.day01.extends_test;
public class Test1 {
public static void main(String[] args) {
Student student = new Student();
student.lookCourse();
student.learning();
student.setName("zhenxi");
System.out.println(student.getName());
student.setClassName("电子信息");
System.out.println(student.getClassName());
Teacher teacher = new Teacher();
teacher.lookCourse();
teacher.releaseIssues();
teacher.setName("努力");
System.out.println(teacher.getName());
teacher.setDepartment("计控学院");
System.out.println(teacher.getDepartment());
}
}

2.2 继承的特点

  • 子类可以继承父类的属性和行为,但是子类不能继承父类的构造器。

  • Java是单继承模式,一个类只能继承一个直接父类。

  • Java不支持多继承、但支持多层继承。

  • Java的所有类,要么直接继承Object类,要么间接继承Object类。

  • 子类可以直接使用父类的静态成员,但不是子类继承了父类的静态成员,而是父类共享给子类。

  • 子类可以继承父类的私有成员,但是不能直接访问。

2.3 继承后访问成员

遵循就近原则

  • 先在子类局部范围查找。
  • 然后在子类成员范围查找。
  • 然后在父类成员范围查找,如果在父类成员中没有找到则报错。
package com.liu.day01.extends_test;
import java.util.Calendar;
public class Test2 {
public static void main(String[] args) {
Cat cat = new Cat();
cat.eat();
cat.showId();
}
}
class Animal{
//父类范围
public int id = 10;
public void eat(){
System.out.println("吃东西~");
}
}
class Cat extends Animal{
//子类范围
public int id = 11;
public void eat(){
System.out.println("吃猫粮");
}
public void showId(){
//局部
int id = 12;
System.out.println(id);
}
}

2.4 方法重写

在继承中,子类出现了和父类中一模一样的方法声明,我们就称子类这个方法是重写的方法。

重写注意事项:

  • 重写方法都加上@Override注解。
  • 重写方法的名称、形参列表必须与被重写方法的名称和参数列表一致。
  • 父类的私有方法、静态方法不能被重写。
  • 子类重写父类方法时,访问权限必须大于或者等于父类被重写的方法的权限。
package com.liu.day01;
public class Override_Test {
public static void main(String[] args) {
NewPhone newPhone = new NewPhone();
newPhone.call();
newPhone.setMessage();
}
}
class OldPhone{
public void call(){
System.out.println("打电话");
}
public void setMessage(){
System.out.println("发送消息");
}
}
class NewPhone extends OldPhone{
@Override
public void call(){
System.out.println("打电话");
System.out.println("视频通话");
}
@Override
public void setMessage(){
System.out.println("发送消息");
System.out.println("发送图片");
}
}

2.5 子类构造器特点

子类中的所有构造器默认都会先访问父类中的无参构造,然后再执行自己的构造器。

package com.liu.day01.constructor;
public class Test1 {
public static void main(String[] args) {
Student student = new Student();
Student student1 = new Student(1);
}
}
class People{
private String name;
public People(){
System.out.println("父类无参构造执行");
}
}
class Student extends People{
private int id;
public Student() {
System.out.println("子类无参构造执行");
}
public Student(int id){
this.id = id;
System.out.println("子类有参构造执行");
}
}
运行结果:
父类无参构造执行
子类无参构造执行
父类无参构造执行
子类有参构造执行

子类构造器如何访问父类有参构造器?

通过使用super来调用父类构造器来初始化继承自父类的数据。

package com.liu.day01.constructor;
public class Test2 {
public static void main(String[] args) {
Zi zi = new Zi("zhenxi", 23);
}
}
class Fu{
private String name;
private int id;
public Fu() {
}
//父类的有参构造
public Fu(String name, int id) {
this.name = name;
this.id = id;
}
}
class Zi extends Fu{
public Zi() {
}
//使用super()初始化父类有参构造中的参数
public Zi(String name, int id) {
super(name, id);
}
}

2.6 this和super

关键字访问成员变量访问成员方法访问构造方法
thisthis.成员变量 访问本类成员变量this.成员方法 访问本类成员方法this(…) 访问本类构造器
supersuper.成员变量 访问父类成员变量super.成员方法 访问父类成员方法super(…) 访问父类构造器

注意:this(…)、super(…)使用注意点

  • 子类通过this(…)去调用本类的其它构造器,本类其它构造器会通过super手动调用父类构造器,最终还是会调用父类的构造器。
  • this(…) super(…)都只能放在构造器的第一行,二者不能共存在同一构造器中。

学习记录2


1 导包

  • 相同包下的类可以直接访问,不同包下的类必须导包,才可以使用,导包格式: import 包名.类名;
  • 假如一个类中需要用到不同类,而这个两个类的名称是一样的, 那么默认只能导入一个类,另一个类要带包名访问。
image-20220720162956032

2 权限修饰符

  • 主要是用来控制一个成员能够被访问的范围。

  • 可以修饰成员变量,方法,构造器,内部类,不同权限修饰符修饰的成员能够被访问的范围将受到限制。

  • 权限修饰符:有四种作用范围由小到大(private ->缺省-> protected -> public )

  • 在同一类下,四种权限修饰符修饰的变量和方法都可以在另一个方法中被调用。

package com.liu.day02.authority_Test;
public class Fu {
private void privatePrint(){
System.out.println("私有权限");
}
void print(){
System.out.println("缺省权限");
}
protected void protectedPrint(){
System.out.println("受保护权限");
}
public void publicPrint(){
System.out.println("公共权限");
}
//同一个类中可以全部访问
public static void main(String[] args) {
Fu fu = new Fu();
fu.privatePrint();
fu.print();
fu.protectedPrint();
fu.publicPrint();
}
}
  • 在同一包下(邻居关系,无继承),public、protected、default修饰的变量和方法都可以在同一包下另一类中被调用。private修饰的不可以。
package com.liu.day02.authority_Test;
public class Student {
public static void main(String[] args) {
//同一个包中的其它类:不能访问私有
Fu fu = new Fu();
fu.protectedPrint();
fu.print();
fu.publicPrint();
}
}
  • 不同包下,但是有继承关系。public、protected修饰的变量和方法都可以在同一包下另一类中被调用。default、private修饰的不可以。
package com.liu.day02.authority_Test2;
import com.liu.day02.authority_Test.Fu;
public class Zi extends Fu{
public static void main(String[] args) {
//不同包下的子类能够访问受保护的以及公有的
Zi zi = new Zi();
zi.protectedPrint();
zi.publicPrint();
}
}
  • 不同包,且非子类public修饰的变量和方法都可以在同一包下另一类中被调用。protected、default、private修饰的不可以。
package com.liu.day02.authority_Test2;
import com.liu.day02.authority_Test.Fu;
public class Teacher {
public static void main(String[] args) {
//不同包,且无关系类,只能使用公有权限。
Fu fu = new Fu();
fu.publicPrint();
}
}

3 final关键字

final的作用

  • final关键字是最终的意思,可以修饰(类、方法、变量)。
  • 修饰类: 表明该类是最终类,不能被继承。
  • 修饰方法: 表明该方法是最终方法,不能被重写。
  • 修饰变量: 表示该变量第一次赋值后,不能再次被赋值(有且仅能被赋值一次)。

final修饰变量的注意

  • final修饰的变量是基本类型:那么变量存储的数据值不能发生改变。
  • final修饰的变量是引用类型:那么变量存储的地址值不能发生改变,但是地址指向的对象内容是可以发生变化的。

4 常量

  • 常量是使用了public static final修饰的成员变量,必须有初始化值,而且执行的过程中其值不能被改变。

  • 常量的作用和好处:可以用于做系统的配置信息,方便程序的维护,同时也能提高可读性。

  • 常量命名规范:英文单词全部大写,多个单词下划线连接起来。

常量的执行原理

  • 在编译阶段会进行“宏替换”,把使用常量的地方全部替换成真实的字面量。
  • 这样做的好处是让使用常量的程序的执行性能与直接使用字面量是一样的。

5 枚举类型

  • 枚举是Java中的一 种特殊类型。
  • 枚举的作用: “是为了做信息的标志和信息的分类”。
image-20220720195021421
public final class Season extends java.lang.Enum<Season> {
public static final Season SPRING;
public static final Season SUMMER;
public static final Season AUTUMN;
public static final Season WINTER;
public static Season[] values();
public static Season valueOf(java.lang.String);
static {};
}

枚举的特征:

  • 枚举类都是继承了枚举类型: java.lang.Enum
  • 枚举都是最终类,不可以被继承。
  • 构造器的构造器都是私有的,枚举对外不能创建对象。
  • 枚举类的第一行默认都是罗列枚举对象的名称的。
  • 枚举类相当于是多例模式。

6 抽象类

6.1 抽象类的定义

  • 在Java中abstract是抽象的意思,可以修饰类、成员方法。
  • abstract修饰类,这个类就是抽象类;修饰方法,这个方法就是抽象方法。
  • 抽象方法只有方法签名,不能声明方法体。
  • 一个类中如果定义了抽象方法,这个类必须声明成抽象类,否则报错。
image-20220720214155219

6.2 抽象类使用场景

  • 抽象类可以理解成不完整的设计图,一般作为父类,让子类来继承。
  • 当父类知道子类一定要完成某些行为,但是每个子类该行为的实现又不同,于是该父类就把该行为定义成抽象方法的形式,具体实现交给子类去完成。此时这个类就可以声明成抽象类。
image-20220720221239935
  • 一个类如果继承了抽象类,那么这个类必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。
image-20220720221827861

案例:

image-20220720222020428
  • Card类
package com.liu.day02.abstract_Test;
public abstract class Card {
private String userName;
private Double money;
public abstract void pay(double payMoney);
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
}
  • ColdenCard类
package com.liu.day02.abstract_Test;
public class GoldenCard extends Card{
@Override
public void pay(double payMoney) {
payMoney = 0.8 * payMoney;
System.out.println("应支付"+payMoney);
super.setMoney(super.getMoney() - payMoney);
System.out.println("余额"+ super.getMoney());
}
}
  • SilverCard类
package com.liu.day02.abstract_Test;
public class SilverCard extends Card{
@Override
public void pay(double payMoney) {
payMoney = 0.85 * payMoney;
System.out.println("应支付"+payMoney);
super.setMoney(super.getMoney() - payMoney);
System.out.println("余额"+ super.getMoney());
}
}
  • 测试:
package com.liu.day02.abstract_Test;
public class Test1 {
public static void main(String[] args) {
GoldenCard goldenCard = new GoldenCard();
goldenCard.setMoney(1000.0);
goldenCard.pay(200);
SilverCard silverCard = new SilverCard();
silverCard.setMoney(1000.0);
silverCard.pay(200);
}
}
  • 输出结果
应支付160.0
余额840.0
应支付170.0
余额830.0

6.3 抽象类的特征和注意事项

  • 类有的成员(成员变量、方法、构造器)抽象类都具备。
  • 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类。
  • 一个类继承了抽象类必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。
  • 不能用abstract修饰变量、代码块、构造器。
  • 最重要的特征:得到了抽象方法,失去了创建对象的能力(有得有失)

6.4 模板方法模式

使用场景说明:当系统中出现同一个功能多处在开发,而该功能中大部分代码是一样的,只有其中部分可能不同的时候。

模板方法模式实现步骤

  • 把功能定义成一个所谓的模板方法,放在抽象类中,模板方法中只定义通用且能确定的代码。
  • 模板方法中不能决定的功能定义成抽象方法让具体子类去实现。

案例需求:

image-20220721090732140
  • Student类
package com.liu.day02.abstract_Test1;
public abstract class Student {
//将该方法加上final关键字,使其变成最终方法
public final void write(){
System.out.println("题目:《我的爸爸》");
System.out.println("我的爸爸是一个普通的平凡人。");
System.out.println(text());
System.out.println("我爱我的爸爸。");
}
//编写抽象方法
public abstract String text();
}
  • StudentPupil类
package com.liu.day02.abstract_Test1;
public class StudentPupil extends Student{
@Override
public String text() {
return "谢谢你,父亲。";
}
}
  • StudentMiddle类
package com.liu.day02.abstract_Test1;
public class StudentMiddle extends Student{
@Override
public String text() {
return "愿您一生健康。";
}
}
  • Test1类
package com.liu.day02.abstract_Test1;
public class Test1 {
public static void main(String[] args) {
StudentMiddle studentMiddle = new StudentMiddle();
studentMiddle.write();
StudentPupil studentPupil = new StudentPupil();
studentPupil.write();
}
}

模板方法模式解决了什么问题?

  • 提高了代码的复用性。
  • 模板方法已经定义了通用结构,模板方法把不能确定的部分定义成抽象方法,交给子类实现,因此,使用者只需要关心自己需要实现的功能即可。

7 接口

7.1 接口定义

image-20220721100706883

7.2 接口的用法

  • 接口是用来被类实现(implements) 的,实现接口的类称为实现类。实现类可以理解成所谓的子类。
  • 一个类实现接口,必须重写完全部接口的全部抽象方法,否则这个类需要定义成抽象类。
image-20220721102049829

7.3 接口小结

  • 类和类的关系: 单继承。
  • 类和接口的关系: 多实现。
  • 接口和接口的关系:多继承,一个接口可以同时继承多个接口。
  • 接口多继承的作用:规范合并,整合多个接口为同一个接口,便于子类实现。
image-20220721103634256

学习记录3


1 多态

1.1 多态的定义

  • 同类型的对象,执行同一个行为,会表现出不同的行为特征。

1.2 多态的前提

  • 有继承/实现关系;有父类引用指向子类对象;有方法重写。

1.3 多态的常见形式

  • 父类类型 对象名称 = new 子类构造器;
  • 接口 对象名称 = new 实现类构造器;
image-20220722090940696

1.4 多态中成员访问特点

  • **方法调用:**编译看左边,运行看右边。
  • **变量调用:**编译看左边,运行也看左边。( 多态侧重行为多态)
image-20220722091646809

1.5 多态的优劣势

优势:

  • 在多态形式下,右边对象可以实现解耦合,便于扩展和维护。
  • 定义方法的时候,使用父类型作为参数,该方法就可以接收这父类的一切子类对象,体现出多态的扩展性与便利。

劣势:

  • 多态下不能使用子类的独有功能。

1.6 多态类型转换

自动类型转换(从子到父):子类对象赋值给父类类型的变量指向。

强制类型转换(从父到子):

  • 此时必须进行强制类型转换: 子类对象变量= (子类)父类类型的变量。
  • 作用:可以解决多态下的劣势,可以实现调用子类独有的功能。
  • 注意:如果转型后的类型和对象真实类型不是同一种类型,那么在转换的时候就会出现ClassCastException
image-20220722093527056
  • Java建议强转转换前使用instanceof判断当前对象的真实类型,再进行强制转换。
image-20220722101247619

1.7 案例

需求:

  • 使用面向对象编程模拟:设计一个电脑对象,可以安装2个USB设备。
  • 鼠标:被安装时可以完成接入、调用点击功能、拔出功能。
  • 键盘:被安装时可以完成接入、调用打字功能、拔出功能。

USB接口实现:

package com.liu.day03.polymorphic;
public interface USB {
void connect();
void unConnect();
}

电脑类实现:

package com.liu.day03.polymorphic;
public class Computer {
public void installUsb(USB usb){
usb.connect();
if (usb instanceof Mouse){
Mouse mouse = (Mouse) usb;
mouse.click();
}else if (usb instanceof Keyboard){
Keyboard keyboard = (Keyboard) usb;
keyboard.typing();
}
usb.unConnect();
}
}

鼠标类实现:

package com.liu.day03.polymorphic;
public class Mouse implements USB{
@Override
public void connect() {
System.out.println("鼠标连接成功");
}
public void click(){
System.out.println("鼠标点击功能");
}
@Override
public void unConnect() {
System.out.println("鼠标断开连接");
}
}

键盘类实现:

package com.liu.day03.polymorphic;
public class Keyboard implements USB{
@Override
public void connect() {
System.out.println("键盘连接成功");
}
public void typing(){
System.out.println("键盘打字功能");
}
@Override
public void unConnect() {
System.out.println("键盘断开连接");
}
}

测试:

package com.liu.day03.polymorphic;
public class Test2 {
public static void main(String[] args) {
USB usb1 = new Mouse();
USB usb2 = new Keyboard();
Computer computer = new Computer();
computer.installUsb(usb1);
computer.installUsb(usb2);
}
}
运行结果:
鼠标连接成功
鼠标点击功能
鼠标断开连接
键盘连接成功
键盘打字功能
键盘断开连接
Process finished with exit code 0

2 内部类

2.1 内部类定义

  • 内部类就是定义在一个类里面的类,里面的类可以理解成(寄生),外部类可以理解成(宿主)。

2.2 内部类的使用场景、作用

  • 当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构可以选择使用内部类来设计。
  • 内部类通常可以方便访问外部类的成员,包括私有的成员。
  • 内部类提供了更好的封装性,内部类本身就可以用private protectecd等修饰,封装性可以做更多控制。

2.3 静态内部类 了解即可

  • 有static修饰,属于外部类本身。
  • 它的特点和使用与普通类是完全一样的, 类有的成分它都有,只是位置在别人里面而已。
  • 可以直接访问外部类的静态成员,不能直接访问外部类的实例成员。
package com.liu.day03.innerClass;
public class StaticInnerClass {
private String className;
public static int classId;
public final int CLASS_ID = 2;
//静态内部类
public static class InClass{
//实例成员
private String name;
public InClass() {
}
public InClass(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//静态成员
public static int id;
//常量
public final int USER_ID = 12;
//静态内部类能调用主类
public void getInnerClass(){
//只能调用静态成员~
classId = 10;
}
}
public static void main(String[] args) {
//创建静态内部类对象的格式
InClass aClass = new StaticInnerClass.InClass();
}
}

总结:静态内部类可以把其当作一个静态成员,静态成员只能访问静态的成员不能访问实例成员。

2.4 实例内部类 了解即可

  • 无static修饰,属于外部类的对象。
  • 可以直接访问外部类的静态成员,实例方法中可以直接访问外部类的实例成员。
  • 成员内部类创建对象:外部类名.内部类名对象名= new外部类构造器.new内部类枸造器();
package com.liu.day03.innerClass;
public class OnClass {
private String name;
private static int id;
public static void setId(){
id = 12;
}
//实例内部类
public class InnerClass{
private String inName;
//这里使用的是8版本 所以不能在实例内部类中创建静态成员
//public static int inId;
public void getOnClass(){
//可以调用实例成员
name = "zhenxi";
//可以调用静态成员
id = 13;
setId();
}
}
public static void main(String[] args) {
//实例内部类创建对象的格式
InnerClass innerClass = new OnClass().new InnerClass();
innerClass.getOnClass();
}
}
  • **注意:**在成员内部类中访问所在外部类对象,格式:外部类名.this
package com.liu.day03.innerClass;
public class People {
private int heartbeat = 90;
public class Heart{
private int heartbeat = 100;
public void show(){
int heartbeat = 120;
System.out.println(heartbeat); //调用局部变量
System.out.println(this.heartbeat);//调用内部类的实例对象
System.out.println(People.this.heartbeat);//调用外部类的实例对象
}
}
public static void main(String[] args) {
Heart heart = new People().new Heart();
heart.show();
}
}
/*运行结果
120
100
90
*/

2.5 局部内部类 了解即可

  • 局部内部类放在方法、代码块、构造器等执行体中。
package com.liu.day03.innerClass;
public class Test {
static {
//静态代码块中的局部内部类
class Dog{
private String dogName;
public Dog() {
}
public Dog(String dogName) {
this.dogName = dogName;
}
}
}
public static void main(String[] args) {
//局部内部类 创建于main方法中
class Cat{
private String name;
private int id;
public Cat() {
}
public Cat(String name, int id) {
this.name = name;
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
Cat cat = new Cat("橘猫",2);
}
}
  • 局部内部类的类文件名为:外部类$N内部类.class。
image-20220722170341684

2.6 匿名内部类 重点

  • 本质上是一个没有名字的局部内部类,定义在方法中、代码块中等。

  • 作用:方便创建子类对象,最终目的为了简化代码编写。

  • 匿名内部类是一个没有名字的内部类。

  • 匿名内部类写出来就会产生一个匿名内部类的对象。

  • 匿名内部类的对象类型相当于是当前new的那个的类型的子类类型。

  • 匿名内部类可以作为方法的实际参数进行传输。

例一:

package com.liu.day03.innerClass;
public class Test2 {
public static void main(String[] args) {
Swimming s = new Swimming() {
@Override
public void swim() {
System.out.println("学生快乐的自由泳");
}
};
go(s);
Swimming s1 = new Swimming() {
@Override
public void swim() {
System.out.println("老师贼快~~~~~");
}
};
go(s1);
System.out.println("--------------");
go(new Swimming() {
@Override
public void swim() {
System.out.println("运动员🏊的贼快啊~~~~~");
}
});
}
public static void go(Swimming s){
System.out.println("开始。。。");
s.swim();
System.out.println("结束。。。");
}
}
interface Swimming{
void swim();
}

例二:swing编程中的应用

package com.liu.day03.innerClass;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Test3 {
public static void main(String[] args) {
// 1、创建窗口
JFrame win = new JFrame("登录界面");
JPanel panel = new JPanel();
win.add(panel);
// 2、创建一个按钮对象
JButton btn = new JButton("登录");
// 注意:讲解匿名内部类的使用
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(win, "点击事件");
}
});
// btn.addActionListener( e -> JOptionPane.showMessageDialog(win, "简略的匿名内部类") );
// 3、把按钮对象添加到桌布上展示
panel.add(btn);
// 4、展示窗口
win.setSize(400, 300);
win.setLocationRelativeTo(null);
win.setVisible(true);
}
}

3 Object类

Object类的作用:

  • 一个类要么默认继承了0bject类,要么间接继承了0bject类,0bject类是Java中的祖宗类。
  • Object类的方法是一切子类都可以直接使用的。

Object类中的常用方法:

方法名说明
pubilc String toString()Returns a string representation of the object.
默认是返回当前对象在堆内存中的地址信息:类的全限名@内存地址
public Boolean equals(Object obj)Indicates whether some other object is “equal to” this one.
默认是比较当前对象与另一个对象的地址是否相同,相同返回true,不同返回false

当未重写toString以及equals方法时:

Student类:

package com.liu.day03.object_Test;
public class Student {
private String name;
private int id;
public Student(String name, int id) {
this.name = name;
this.id = id;
}
public Student() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}

测试类:Test:

package com.liu.day03.object_Test;
public class Test {
public static void main(String[] args) {
Student student = new Student("zhenxi",2022935775);
Student student1 = new Student("成龙",2022935776);
System.out.println(student.toString());
System.out.println(student.equals(student1));
}
}
/**
运行结果:
com.liu.day03.object_Test.Student@1540e19d
false
*/

当重写toString方法和equals方法:

@Override
public boolean equals(Object o) {
//首先判断传进来的对象是不是原对象
if (this == o) return true;
//判断传进来的对象是否为空,以及判断是否与本对象是不是同一个类型
if (o == null || getClass() != o.getClass()) return false;
//将传进来的对象转成Student类型对象
Student student = (Student) o;
//判断对象中的内容是否相同
return id == student.id && Objects.equals(name, student.name);
}
//将对象的内容输出出去
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", id=" + id +
'}';
}

再次进行测试:

package com.liu.day03.object_Test;
public class Test {
public static void main(String[] args) {
Student student = new Student("zhenxi",2022935777);
Student student1 = new Student("成龙",2022935776);
System.out.println(student.toString());
System.out.println(student1.toString());
System.out.println(student.equals(student));
System.out.println(student.equals(student1));
}
}
/**
运行结果:
Student{name='zhenxi', id=2022935775}
Student{name='成龙', id=2022935776}
true
false
*/

4 Objects类

Objects仍然继承Object类。该类最关键的就是它的equals方法,官方在进行字符串比较时,没有对象自己的equals方法,而是选择了Objects的equals方法来比较两个对象。在使用Objects的equals方法比较两个对象时,底层会先进行非空判断,从而可以避免空指针异常,再进行equals比较。

方法名说明
public static boolean equals(Object a,Object b)Returns true if the arguments are equal to each other and false otherwise.

这是Objects类的equals方法:

public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}

主要用于解决两个对象进行比较时出现NullPointerException这个问题:

可以看到阿里的Java开发手册里的强制要求:

image-20220724182239344
public class Test3 {
public static void main(String[] args) {
String s1 = null;
String s2 = "liu";
s1.equals(s2);
}
}
/**
运行结果
Exception in thread "main" java.lang.NullPointerException
at com.liu.day03.object_Test.Test3.main(Test3.java:7)
*/

当使用Objects的静态equals方法时:

public class Test3 {
public static void main(String[] args) {
String s1 = null;
String s2 = "liu";
System.out.println(Objects.equals(s1, s2));
}
}
/**
运行结果:
false
*/

5 StringBuilder类

  • StringBuilder是一个可变的字符串类,我们可以把它看成是一个对象容器。
  • 作用:提高字符串的操作效率,如拼接、修改等。
StringBuilder构造器说明
public StringBuilder()Constructs a string builder with no characters in it and an initial capacity of 16 characters.
构造一个其中没有字符且初始容量为 16 个字符的字符串构建器。
public StringBuilder(String str)Constructs a string builder initialized to the contents of the specified string.
构造一个初始化为指定字符串内容的字符串构建器。
StringBuilder常用方法说明
public StringBuilder append(任意类型)Appends the string representation of the xxxx argument to the sequence.
xxxx 参数的字符串表示形式附加到序列中。
public StringBuilder reverse()Causes this character sequence to be replaced by the reverse of the sequence.
导致此字符序列被相反的序列替换。
public int length()Returns the length (character count).
返回长度
public String toString()Returns a string representing the data in this sequence.
返回表示此序列中数据的字符串。

方法使用:

public class Test {
public static void main(String[] args) {
StringBuilder stringBuilder = new StringBuilder();
StringBuilder str = new StringBuilder("zhenxi");
str.append(2022).append(935).append(775).append("珍惜大学");
//注意最终还是需要通过toString转换成String类型
System.out.println(str.toString());
System.out.println(str.reverse());
System.out.println(str.length());
}
}
/**
运行结果:
zhenxi2022935775珍惜大学
学大尔哈齐齐5775392202畅刘
18
*/

与String类比较图:

image-20220724160640831 image-20220724160744220

总结:

  • String:内容是不可变的、拼接字符串性能差。
  • StringBuilder:内容是可变的、拼接字符串性能好、代码优雅。

需求:

设计一个方法用于输出任意整型数组的内容,要求输出成如下格式:”该数组内容为: [11, 22, 33, 44,55]”

package com.liu.day03.object_Test;
public class StringBuilderTest2 {
public static void main(String[] args) {
int[] arr1 = null;
System.out.println(toString(arr1));
int[] arr2 = {10, 22, 19};
System.out.println(toString(arr2));
int[] arr3 = {};
System.out.println(toString(arr3));
}
// 1、定义方法接收任意整型数组,返回数组内容格式
public static String toString(int[] arr){
if(arr != null){
// 2、开始拼接内容。
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < arr.length; i++) {
sb.append(arr[i] ).append(i == arr.length - 1 ? "" : ", ");
}
sb.append("]");
return sb.toString();
}else {
return null;
}
}
}

6 Math类

Math类的常用方法说明
public static int abs(int a)Returns the absolute value of an int value.
返回 int 值的绝对值。
public static double ceil(double a)Returns the smallest (closest to negative infinity) double value that is greater than or equal to the argument and is equal to a mathematical integer.
返回大于或等于参数且等于数学整数的最小(最接近负无穷大)double 值。
public static double floor(double a)Returns the largest (closest to positive infinity) double value that is less than or equal to the argument and is equal to a mathematical integer.Returns the length (character count).
返回小于或等于参数且等于数学整数的最大(最接近正无穷大)double 值。返回长度(字符数)。
public static int round(float a)Returns the closest int to the argument, with ties rounding to positive infinity.
返回最接近参数的 int,并舍入为正无穷大。
public static int max(int a, int b)Returns the greater of two int values.
返回两个 int 值中的较大者。
public static int min(int a, int b)Returns the smaller of two int values.
返回两个 int 值中较小的一个。
static double pow(double a, double b)Returns the value of the first argument raised to the power of the second argument.
返回第一个参数的第二个参数次幂的值。
static double random()Returns a double value with a positive sign, greater than or equal to 0.0 and less than 1.0.
返回一个带正号的“double”值,大于或等于“0.0”且小于“1.0”
package com.liu.day03.math;
public class Test {
public static void main(String[] args) {
System.out.println(Math.abs(-1));
System.out.println(Math.ceil(1.01));
System.out.println(Math.ceil(1.54));
System.out.println(Math.floor(3.01));
System.out.println(Math.floor(3.89));
System.out.println(Math.max(12, 10));
System.out.println(Math.min(12, 10));
System.out.println(Math.pow(2, 5));
int a = (int)(Math.random()*100)+1;
System.out.println(a);
}
}
/**
运行结果:
1
2.0
2.0
3.0
3.0
12
10
32.0
29
Process finished with exit code 0
*/

7 System类

和Math类一样,不能实例化,直接用类名调用即可。

System常用方法说明
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)Copies an array from the specified source array, beginning at the specified position, to the specified position of the destination array.
将指定源数组中的数组从指定位置开始复制到目标数组的指定位置。
public static long currentTimeMillis()Returns the current time in milliseconds.
以毫秒为单位返回当前时间。
public class Test {
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
System.out.println(startTime);
for (int i = 0; i < 100000; i++) {
System.out.println("i:"+i);
}
long endTime = System.currentTimeMillis();
System.out.println("运行时间为:"+(endTime-startTime)/1000.0+"s");
}
}
/**
i:99999
运行时间为:1.141s
*/
package com.liu.day03.System;
import java.util.Arrays;
public class SystemDemo {
public static void main(String[] args) {
/**
arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length)
参数一:被拷贝的数组
参数二:从哪个索引位置开始拷贝
参数三:复制的目标数组
参数四:粘贴位置
参数五:拷贝元素的个数
*/
int[] arr1 = {10, 20, 30, 40, 50, 60, 70};
int[] arr2 = new int[6]; // [0, 0, 0, 0, 0, 0] ==> [0, 0, 40, 50, 60, 0]
System.arraycopy(arr1, 3, arr2, 2, 3);
System.out.println(Arrays.toString(arr2));
}
}
/**
运行结果:
[0, 0, 40, 50, 60, 0]
*/

8 BigDecima类

浮点型运算的时候直接加减乘除时可能会出现数据失真(精度问题)。用来对超过16位有效位的数进行精确的计算。此类可以完成大的小数操作,而且也可以使用此类进行精确的四舍五入。从而解决浮点型运算数据失真的问题。

BigDecima类的常用方法说明
public BigDecimal add(BigDecimal augend)加法
public BigDecimal subtract(BigDecimal subtrahend)减法
public BigDecimal multiply(BigDecimal multiplicand)乘法
public BigDecimal divide(BigDecimal divisor)除法

关于BigDecima类的一些注意事项:(图片中的内容来自阿里Java开发规范)

image-20220724182432283 image-20220724182523857
package com.liu.day03.System;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.NumberFormat;
public class BigDecimalDemo {
public static void main(String[] args) {
// 浮点型运算的时候直接+ * / 可能会出现数据失真(精度问题)。
System.out.println(0.09 + 0.01);
System.out.println(1.0 - 0.32);
System.out.println(1.015 * 100);
System.out.println(1.301 / 100);
System.out.println("-------------------------");
double a = 0.1;
double b = 0.2;
double c = a + b;
System.out.println(c);
System.out.println("--------------------------");
// 包装浮点型数据成为大数据对象 BigDeciaml
BigDecimal a1 = BigDecimal.valueOf(a);
BigDecimal b1 = BigDecimal.valueOf(b);
BigDecimal c1 = a1.add(b1);
// BigDecimal c1 = a1.subtract(b1);
// BigDecimal c1 = a1.multiply(b1);
// BigDecimal c1 = a1.divide(b1);
System.out.println(c1);
// 目的:double
double rs = c1.doubleValue();
System.out.println(rs);
// 注意事项:BigDecimal是一定要精度运算的
BigDecimal a11 = BigDecimal.valueOf(10.0);
BigDecimal b11 = BigDecimal.valueOf(3.0);
/**
参数一:除数 参数二:保留小数位数 参数三:舍入模式
*/
BigDecimal c11 = a11.divide(b11, 2, RoundingMode.HALF_UP); // 3.3333333333
System.out.println(c11);
}
}

学习记录4


1 Date类

Date类的对象在Java中代表的是当前所在系统的此刻日期时间。

image-20220726145159635

**案例:**请计算出当前时间往后走1小时121秒之后的时间是多少。

package com.liu.day04.dateDemo;
import java.util.Date;
public class DateDemo01 {
public static void main(String[] args) {
//Date的无参构造
Date date = new Date();
System.out.println(date);
long time = date.getTime();
System.out.println(time);
System.out.println(System.currentTimeMillis());
//有参构造器
System.out.println(new Date(time));
//请计算出当前时间往后走1小时121秒之后的时间是多少。
long time2 = System.currentTimeMillis();
time2 += (60*60+121)*1000;
//通过Date的有参构造进行转换成日期对象
Date date1 = new Date(time2);
System.out.println(date1);
//或者使用setTime函数
Date date2 = new Date();
date2.setTime(time2);
System.out.println(date2);
}
}

2 SimpleDateFormat类

  • 可以对Date对象或时间毫秒值格式化成我们喜欢的时间形式。
  • 也可以把字符串的时间形式解析成日期对象。
image-20220726144811431

**案例一:**SimpleDateFormat类的基本方法。

package com.liu.day04.simpleDateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
public class SimpleDateFormatDemo {
public static void main(String[] args) {
//日期对象
Date date = new Date();
System.out.println(date);
//格式化日期对象
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss EEE a");
String s = simpleDateFormat.format(date);
System.out.println(s);
//求121秒后的格式日期
long l = System.currentTimeMillis() + 121*1000;
System.out.println(simpleDateFormat.format(l));
}
}
image-20220726145536763

**需求二:**使用上述方法,计算出2021年08月06日11点11分11秒,往后走2天14小时49分06秒后的时间是多少。

package com.liu.day04.simpleDateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class SimpleDateFormatDemo2 {
public static void main(String[] args) throws ParseException {
//请计算出2021年08月06日11点11分11秒,往后走2天14小时49分06秒后的时间是多少。
String dateStr = "2021年08月06日 11:11:11";
//将字符串解析为日期对象
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
Date date = simpleDateFormat.parse(dateStr);
long time = date.getTime()+(2L*24*60*60+14*60*60+49*60+6)*1000;
System.out.println(simpleDateFormat.format(time));
}
}

需求三:

  • 小贾下单并付款的时间为: 2020年11月11日0:03:47
  • 小皮下单并付款的时间为: 2020年11月11日0:10:11
  • 用代码说明这两位同学有没有参加上秒杀活动?
package com.liu.day04.simpleDateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class SimpleDateFormatDemo3 {
public static void main(String[] args) throws ParseException {
//开始时间和结束时间
String startTime="2020年11月11日 00:00:00";
String endTime="2020年11月11日 00:10:00";
//小贾 小皮
String xiaoJia="2020年11月11日 00:03:47";
String xiaoPi="2020年11月11日 00:10:11";
//解析时间
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
Date date1 = simpleDateFormat.parse(startTime);
Date date2 = simpleDateFormat.parse(endTime);
Date date3 = simpleDateFormat.parse(xiaoJia);
Date date4 = simpleDateFormat.parse(xiaoPi);
//时间判断函数
if (date3.after(date1) && date3.before(date2)){
System.out.println("小贾发货成功");
}else {
System.out.println("小贾抢购失败");
}
if (date4.after(date1) && date4.before(date2)){
System.out.println("小皮发货成功");
}else {
System.out.println("小皮抢购失败");
}
}
}

**注意点:**阿里开发手册的规范要遵循

image-20220726150421553

3 Calendar类

  • Calendar代表了系统此刻日期对应的日历对象。
  • Calendar是一个抽象类,不能直接创建对象。
image-20220726145834767

**案例:**Calendar类的常用方法使用。

package com.liu.day04.simpleDateFormat;
import java.util.Calendar;
import java.util.Date;
public class CalendarDemo {
public static void main(String[] args) {
//获取日历对象
Calendar calendar = Calendar.getInstance();
System.out.println(calendar);
//获取日历信息
System.out.println(calendar.get(Calendar.YEAR));
System.out.println(calendar.get(Calendar.MONTH)+1);
System.out.println(calendar.get(Calendar.DAY_OF_MONTH));
//修改日历的信息
calendar.set(Calendar.YEAR,2023);
System.out.println(calendar.get(Calendar.YEAR));
//64天2分后的日历
calendar.add(Calendar.DAY_OF_MONTH,64);
calendar.add(Calendar.MINUTE,2);
Date time = calendar.getTime();
System.out.println(time);
}
}

4 新增日期API

image-20220726150934535

4.1 LocalTime/LocalDate/LocalDateTime

image-20220726151009975
  • LocalDate常用方法:
package com.liu.day04.dateDemo;
import java.time.LocalDate;
import java.time.Month;
public class Demo01LocalDate {
public static void main(String[] args) {
// 1、获取本地日期对象。
LocalDate nowDate = LocalDate.now();
System.out.println("今天的日期:" + nowDate);//今天的日期:
int year = nowDate.getYear();
System.out.println("year:" + year);
int month = nowDate.getMonthValue();
System.out.println("month:" + month);
int day = nowDate.getDayOfMonth();
System.out.println("day:" + day);
//当年的第几天
int dayOfYear = nowDate.getDayOfYear();
System.out.println("dayOfYear:" + dayOfYear);
//星期
System.out.println(nowDate.getDayOfWeek());
System.out.println(nowDate.getDayOfWeek().getValue());
//月份
System.out.println(nowDate.getMonth());//AUGUST
System.out.println(nowDate.getMonth().getValue());//8
LocalDate bt = LocalDate.of(1991, 11, 11);
System.out.println(bt);//直接传入对应的年月日
System.out.println(LocalDate.of(1991, Month.NOVEMBER, 11));//相对上面只是把月换成了枚举
}
}
  • LocalTime常用方法:
package com.liu.day04.dateDemo;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
public class Demo02LocalTime {
public static void main(String[] args) {
// 1、获取本地时间对象。
LocalTime nowTime = LocalTime.now();
System.out.println("今天的时间:" + nowTime);//今天的时间:
int hour = nowTime.getHour();//时
System.out.println("hour:" + hour);//hour:
int minute = nowTime.getMinute();//分
System.out.println("minute:" + minute);//minute:
int second = nowTime.getSecond();//秒
System.out.println("second:" + second);//second:
int nano = nowTime.getNano();//纳秒
System.out.println("nano:" + nano);//nano:
System.out.println(LocalTime.of(8, 20));//时分
System.out.println(LocalTime.of(8, 20, 30));//时分秒
System.out.println(LocalTime.of(8, 20, 30, 150));//时分秒纳秒
LocalTime mTime = LocalTime.of(8, 20, 30, 150);
System.out.println(LocalDateTime.of(1991, 11, 11, 8, 20));
System.out.println(LocalDateTime.of(1991, Month.NOVEMBER, 11, 8, 20));
System.out.println(LocalDateTime.of(1991, 11, 11, 8, 20, 30));
System.out.println(LocalDateTime.of(1991, Month.NOVEMBER, 11, 8, 20, 30));
System.out.println(LocalDateTime.of(1991, 11, 11, 8, 20, 30, 150));
System.out.println(LocalDateTime.of(1991, Month.NOVEMBER, 11, 8, 20, 30, 150));
}
}
  • LocalDateTime常用方法:
image-20220726152154473
package com.liu.day04.dateDemo;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
public class Demo03LocalDateTime {
public static void main(String[] args) {
// 日期 时间
LocalDateTime nowDateTime = LocalDateTime.now();
System.out.println("今天是:" + nowDateTime);//今天是:
System.out.println(nowDateTime.getYear());//年
System.out.println(nowDateTime.getMonthValue());//月
System.out.println(nowDateTime.getDayOfMonth());//日
System.out.println(nowDateTime.getHour());//时
System.out.println(nowDateTime.getMinute());//分
System.out.println(nowDateTime.getSecond());//秒
System.out.println(nowDateTime.getNano());//纳秒
//日:当年的第几天
System.out.println("dayOfYear:" + nowDateTime.getDayOfYear());//dayOfYear:249
//星期
System.out.println(nowDateTime.getDayOfWeek());//THURSDAY
System.out.println(nowDateTime.getDayOfWeek().getValue());//4
//月份
System.out.println(nowDateTime.getMonth());//SEPTEMBER
System.out.println(nowDateTime.getMonth().getValue());//9
LocalDate ld = nowDateTime.toLocalDate();
System.out.println(ld);
LocalTime lt = nowDateTime.toLocalTime();
System.out.println(lt.getHour());
System.out.println(lt.getMinute());
System.out.println(lt.getSecond());
}
}

4.2 Instant

image-20220726152431817
package com.liu.day04.dateDemo;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Date;
public class Demo05Instant {
public static void main(String[] args) {
// 1、得到一个Instant时间戳对象
Instant instant = Instant.now();
System.out.println(instant);
// 2、系统此刻的时间戳怎么办?
Instant instant1 = Instant.now();
System.out.println(instant1.atZone(ZoneId.systemDefault()));
// 3、如何去返回Date对象
Date date = Date.from(instant);
System.out.println(date);
Instant i2 = date.toInstant();
System.out.println(i2);
}
}

4.3 DateTimeFormatter

image-20220726152924911
package com.liu.day04.dateDemo;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class Demo06DateTimeFormat {
public static void main(String[] args) {
// 本地此刻 日期时间 对象
LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt);
// 解析/格式化器
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss EEE a");
// 正向格式化
System.out.println(dtf.format(ldt));
// 逆向格式化
System.out.println(ldt.format(dtf));
// 解析字符串时间
DateTimeFormatter dtf1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// 解析当前字符串时间成为本地日期时间对象
LocalDateTime ldt1 = LocalDateTime.parse("2019-11-11 11:11:11" , dtf1);
System.out.println(ldt1);
System.out.println(ldt1.getDayOfYear());
}
}

4.4 Period

image-20220726153214800
package com.liu.day04.dateDemo;
import java.time.LocalDate;
import java.time.Period;
public class Demo07Period {
public static void main(String[] args) {
// 当前本地的年月日
LocalDate today = LocalDate.now();
System.out.println(today);//
// 生日的的年月日
LocalDate birthDate = LocalDate.of(1998, 10, 13);
System.out.println(birthDate);
Period period = Period.between(birthDate, today);//第二个参数减第一个参数
System.out.println(period.getYears());
System.out.println(period.getMonths());
System.out.println(period.getDays());
}
}

4.5 Duration

image-20220726153615990
package com.liu.day04.dateDemo;
import java.time.Duration;
import java.time.LocalDateTime;
public class Demo08Duration {
public static void main(String[] args) {
// 本地日期时间对象。
LocalDateTime today = LocalDateTime.now();
System.out.println(today);
// 出生的日期时间对象
LocalDateTime birthDate = LocalDateTime.of(2022,7,23,01,00,00);
System.out.println(birthDate);
Duration duration = Duration.between(today,birthDate);//第二个参数减第一个参数
System.out.println(duration.toDays());//两个时间差的天数
System.out.println(duration.toHours());//两个时间差的小时数
System.out.println(duration.toMinutes());//两个时间差的分钟数
System.out.println(duration.toMillis());//两个时间差的毫秒数
System.out.println(duration.toNanos());//两个时间差的纳秒数
}
}

4.6 ChronoUnit

image-20220726153822904
package com.liu.day04.dateDemo;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
public class Demo09ChronoUnit {
public static void main(String[] args) {
// 本地日期时间对象:此刻的
LocalDateTime today = LocalDateTime.now();
System.out.println(today);
// 生日时间
LocalDateTime birthDate = LocalDateTime.of(1990,10,1,
10,50,59);
System.out.println(birthDate);
System.out.println("相差的年数:" + ChronoUnit.YEARS.between(birthDate, today));
System.out.println("相差的月数:" + ChronoUnit.MONTHS.between(birthDate, today));
System.out.println("相差的周数:" + ChronoUnit.WEEKS.between(birthDate, today));
System.out.println("相差的天数:" + ChronoUnit.DAYS.between(birthDate, today));
System.out.println("相差的时数:" + ChronoUnit.HOURS.between(birthDate, today));
System.out.println("相差的分数:" + ChronoUnit.MINUTES.between(birthDate, today));
System.out.println("相差的秒数:" + ChronoUnit.SECONDS.between(birthDate, today));
System.out.println("相差的毫秒数:" + ChronoUnit.MILLIS.between(birthDate, today));
System.out.println("相差的微秒数:" + ChronoUnit.MICROS.between(birthDate, today));
System.out.println("相差的纳秒数:" + ChronoUnit.NANOS.between(birthDate, today));
System.out.println("相差的半天数:" + ChronoUnit.HALF_DAYS.between(birthDate, today));
System.out.println("相差的十年数:" + ChronoUnit.DECADES.between(birthDate, today));
System.out.println("相差的世纪(百年)数:" + ChronoUnit.CENTURIES.between(birthDate, today));
System.out.println("相差的千年数:" + ChronoUnit.MILLENNIA.between(birthDate, today));
System.out.println("相差的纪元数:" + ChronoUnit.ERAS.between(birthDate, today));
}
}

5 包装类

image-20220726154147476

包装类的功能:

image-20220726154215548
package com.liu.day04.packingClass;
public class PackingDemo {
public static void main(String[] args) {
int a = 10;//基本类型
Integer a1 = 11;//引用类型
System.out.println(a1);
Integer a2 = 10;//自动装箱
System.out.println(a2);
int a3 = a2;
System.out.println(a3);//自动拆箱
//包装类最有用的:将字符串转化成数字类型
int i = Integer.parseInt("12");
double v = Double.parseDouble("12.31");
System.out.println(i+20);
System.out.println(v+0.11);
//可以不使用上述方法,可以直接只使用valueOf方法
int i1 = Integer.valueOf("12");
double v1 = Double.valueOf("12.31");
System.out.println(i1+20);
System.out.println(v1+0.11);
}
}

6 正则表达式

正则表达式可以用一些规定的字符来制定规则,并用来校验数据格式的合法性。

image-20220726155924858

**案例一:**校验qq号码的正确性。

package com.liu.day04.regex;
public class RegexDemo {
public static void main(String[] args) {
//需求:校验qq号码 必须全部数字6 -20位
System.out.println(checkQQ("773395"));
System.out.println(checkQQ("773395726111"));
System.out.println(checkQQ2("773395"));
System.out.println(checkQQ2("7733"));
}
//不使用正则表达式方法
public static boolean checkQQ(String qq){
if (qq == null || qq.length()<6 || qq.length()>20){
return false;
}
//判断qq中是否全是数字
for (int i = 0; i < qq.length(); i++) {
char c = qq.charAt(i);
if (c<'0' || c>'9'){
return false;
}
}
return true;
}
//使用正则表达式
public static boolean checkQQ2(String qq){
return qq!= null && qq.matches("\\d{6,20}");
}
}

**案例二:**正则表达式一些基本规定的使用

package com.liu.day04.regex;
public class RegexDemo02 {
public static void main(String[] args) {
//public boolean matches(String regex):判断是否与正则表达式匹配,匹配返回true
System.out.println("a".matches("[abc]")); // true
System.out.println("z".matches("[abc]")); // false
// 不能出现a b c ^表示不存在
System.out.println("a".matches("[^abc]")); // false
System.out.println("z".matches("[^abc]")); // true
System.out.println("a".matches("\\d")); // false
System.out.println("3".matches("\\d")); // true
//默认匹配一个字符
System.out.println("111".matches("\\d")); // false
System.out.println("z".matches("\\w")); // true
System.out.println("2".matches("\\w")); // true
//默认匹配一个字符
System.out.println("30".matches("\\w")); // false
//\w判断是否是一个数字
System.out.println("".matches("\\w")); //false
//\W判断一个非单词字符
System.out.println("".matches("\\W")); // true
System.out.println("---------------------------------");
// 以上正则匹配只能校验单个字符。
// 校验密码
// 必须是数字 字母 下划线 至少 6位
System.out.println("23456sqxS".matches("\\w{6,}"));
System.out.println("a557".matches("\\w{6,}"));
// 验证码 必须是数字和字符 必须是4位
System.out.println("135s".matches("[a-zA-Z0-9]{4}"));
System.out.println("553_".matches("[a-zA-Z0-9]{4}"));
System.out.println("886s".matches("[\\w&&[^_]]{4}"));
System.out.println("996_".matches("[\\w&&[^_]]{4}"));
}
}

案例三:

  • 请编写程序模拟用户输入手机号码、验证格式正确,并给出提示,直到格式输入正确为止。
  • 请编写程序模拟用户输入邮箱号码、验证格式正确,并给出提示,直到格式输入正确为止。
  • 请编写程序模拟用户输入电话号码、验证格式正确,并给出提示,直到格式输入正确为止。
package com.liu.day04.regex;
import java.util.Arrays;
import java.util.Scanner;
public class RegexTest3 {
public static void main(String[] args) {
checkPhone();
checkEmail();
checkTel();
}
public static void checkTel(){
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请您输入您的电话号码:");
String tel = sc.next();
if(tel.matches("0\\d{2,6}-?\\d{5,20}")){
System.out.println("格式正确,注册完成!");
break;
}else {
System.out.println("格式有误!");
}
}
}
public static void checkEmail(){
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请您输入您的注册邮箱:");
String email = sc.next();
if(email.matches("\\w{1,30}@[a-zA-Z0-9]{2,20}(\\.[a-zA-Z0-9]{2,20}){1,2}")){
System.out.println("邮箱格式正确,注册完成!");
break;
}else {
System.out.println("格式有误!");
}
}
}
public static void checkPhone(){
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请您输入您的注册手机号码:");
String phone = sc.next();
// 判断手机号码的格式是否正确
if(phone.matches("1[3-9]\\d{9}")){
System.out.println("手机号码格式正确,注册完成!");
break;
}else {
System.out.println("格式有误!");
}
}
}
}
/**
运行结果:
请您输入您的注册手机号码:
13461460555
手机号码格式正确,注册完成!
请您输入您的注册邮箱:
773395777@qq.com
邮箱格式正确,注册完成!
请您输入您的电话号码:
0120-41123333
格式正确,注册完成!
*/

案例四:

image-20220726195619437
package com.liu.day04.regex;
public class RegexDemo04 {
public static void main(String[] args) {
String names = "你好jdlajdl努力少年jdoisajdo相信自己ldjsaoijdo";
String[] arrs = names.split("\\w+");
for (int i = 0; i < arrs.length; i++) {
System.out.println(arrs[i]);
}
String names2 = names.replaceAll("\\w+", " ");
System.out.println(names2);
}
}
/*
运行结果:
你好
努力少年
相信自己
你好 努力少年 相信自己
**/

案例五:

正则表达式支持对信息的爬取(了解即可)

package com.liu.day04.regex;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexDemo05 {
public static void main(String[] args) {
String rs = "来黑马程序学习Java,电话020-43422424,或者联系邮箱" +
"itcast@itcast.cn,电话18762832633,0203232323" +
"邮箱bozai@itcast.cn,400-100-3233 ,4001003232";
// 需求:从上面的内容中爬取出 电话号码和邮箱。
// 1、定义爬取规则,字符串形式
String regex = "(\\w{1,30}@[a-zA-Z0-9]{2,20}(\\.[a-zA-Z0-9]{2,20}){1,2})|(1[3-9]\\d{9})" + "|(0\\d{2,6}-?\\d{5,20})|(400-?\\d{3,9}-?\\d{3,9})";
// 2、把这个爬取规则编译成匹配对象。
Pattern pattern = Pattern.compile(regex);
// 3、得到一个内容匹配器对象
Matcher matcher = pattern.matcher(rs);
// 4、开始寻找对象
while (matcher.find()) {
String rs1 = matcher.group();
System.out.println(rs1);
}
}
}

7 Arrays类

image-20220726200522794
package com.liu.day04.ArrayDemo;
import java.util.Arrays;
public class ArraysDemo1 {
public static void main(String[] args) {
// 目标:学会使用Arrays类的常用API ,并理解其原理
int[] arr = {10, 21, 50, 23, 24, 100};
System.out.println(arr);
// 1、返回数组内容的 toString(数组)
System.out.println(Arrays.toString(arr));
// 2、排序的API(默认自动对数组元素进行升序排序)
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
// 3、二分搜索技术(前提数组必须排好序才支持,否则出bug)
int index = Arrays.binarySearch(arr, 55);
System.out.println(index);
// 返回不存在元素的规律: - (应该插入的位置索引 + 1)
int index2 = Arrays.binarySearch(arr, 22);
System.out.println(index2);
// 注意:数组如果么有排好序,可能会找不到存在的元素,从而出现bug!!
int[] arr2 = {12, 36, 34, 25 , 13, 24, 234, 100};
System.out.println(Arrays.binarySearch(arr2 , 36));
}
}
image-20220726200941494
package com.liu.day04.ArrayDemo;
import java.util.Arrays;
import java.util.Comparator;
public class ArraysDemo2 {
public static void main(String[] args) {
// 目标:自定义数组的排序规则:Comparator比较器对象。
// 1、Arrays的sort方法对于有值特性的数组是默认升序排序
int[] ages = {34, 12, 42, 23};
Arrays.sort(ages);
System.out.println(Arrays.toString(ages));
// 2、需求:降序排序!(自定义比较器对象,只能支持引用类型的排序!!)
Integer[] ages1 = {34, 12, 42, 23};
/**
参数一:被排序的数组 必须是引用类型的元素
参数二:匿名内部类对象,代表了一个比较器对象。
*/
Arrays.sort(ages1, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
// 指定比较规则。
// if(o1 > o2){
// return 1;
// }else if(o1 < o2){
// return -1;
// }
// return 0;
// return o1 - o2; // 默认升序
return o2 - o1; // 降序
}
});
System.out.println(Arrays.toString(ages1));
Student[] students = new Student[3];
students[0] = new Student("吴磊",23 , 175.5);
students[1] = new Student("谢鑫",18 , 185.5);
students[2] = new Student("王亮",20 , 195.5);
System.out.println(Arrays.toString(students));
// Arrays.sort(students); // 没有重写比较器直接运行奔溃
Arrays.sort(students, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
// 自己指定比较规则
// return o1.getAge() - o2.getAge(); // 按照年龄升序排序!
// return o2.getAge() - o1.getAge(); // 按照年龄降序排序!!
// return Double.compare(o1.getHeight(), o2.getHeight()); //比较浮点型可以这样写 升序
return Double.compare(o2.getHeight(), o1.getHeight()); //比较浮点型可以这样写 降序
}
});
System.out.println(Arrays.toString(students));
}
}

8 选择排序、二分查找算法

8.1 选择排序

  • 每轮选择当前位置,开始找出后面的较小值与该位置交换。
  • 确定总共需要选择几轮:数组的长度-1。
  • 控制每轮从以前位置为基准,与后面元素选择几次。
选择排序动态图

选择排序代码实现:

package com.liu.day04.est;
import java.util.Arrays;
public class Test1 {
public static void main(String[] args) {
// 1、定义数组
int[] arr = {5, 1, 3, 2};
// 0 1 2 3
// 2、定义一个循环控制选择几轮: arr.length - 1
for (int i = 0; i < arr.length - 1; i++) {
// 3、定义内部循环,控制选择几次
for (int j = i + 1; j < arr.length; j++) {
// 当前位:arr[i]
// 如果有比当前位数据更小的,则交换
if(arr[i] > arr[j]) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
System.out.println(Arrays.toString(arr));
}
}

8.2 二分查找

  • 定义变量记录左边和右边位置。
  • 使用while循环控制查询( 条件是左边位置<= 右边位置)。
  • 循环内部获取中间元素索引。
  • 判断当前要找的元素如果大于中间元素,左边位置=中间索引+1。
  • 判断当前要找的元素如果小于中间元素,右边位置=中间索引-1。
  • 判断当前要找的元素如果等于中间元素,返回当前中间元素索引。

二分查找代码实现:

package com.liu.day04.est;
public class Test2 {
public static void main(String[] args) {
// 1、定义数组
int[] arr = {10, 14, 16, 25, 28, 30, 35, 88, 100};
System.out.println(binarySearch(arr , 35));
System.out.println(binarySearch(arr , 350));
}
public static int binarySearch(int[] arr, int data){
// 1、定义左边位置 和 右边位置
int left = 0;
int right = arr.length - 1;
// 2、开始循环,折半查询。
while (left <= right){
// 取中间索引
int middleIndex = (left + right) / 2;
// 3、判断当前中间位置的元素和要找的元素的大小情况
if(data > arr[middleIndex]) {
// 往右边找,左位置更新为 = 中间索引+1
left = middleIndex + 1;
}else if(data < arr[middleIndex]) {
// 往左边找,右边位置 = 中间索引 - 1
right = middleIndex - 1;
}else {
return middleIndex;
}
}
return -1; // 没有找到该元素返回-1
}
}

9 Lambda表达式

  • Lambda表达式是JDK 8开始后的一种新语法形式。

  • **作用:**简化匿名内部类的代码写法。

  • 格式:

    (匿名内部类被重写方法的形参列表) -> { 被重写方法的方法体代码。

    }

    注: ->是语法形式,无实际含义

  • 注意: Lambda表达式只能简化函数式接口的匿名内部类的写法形式。

  • 什么是函数式接口?

    1.首先必须是接口、其次接口中有且仅有一个抽象方法的形式。

    2.通常我们会在接口上加上一个@FunctionalInterface注解,标记该接口必须是满足函数式接口。

  • Lambda表达式的使用:

package com.liu.day04.LambdaDemo;
public class LambdaDemo1 {
public static void main(String[] args) {
//Lambda的标准格式简化匿名内部类的代码形式
Dog a = new Dog() {
@Override
public void run() {
System.out.println("狗跑的贼快~~~~~");
}
};
a.run();
}
}
abstract class Dog{
public abstract void run();
}

Lambda表达式的省略写法(进一步在Lambda表达式的基础上继续简化)

  • 参数类型可以省略不写。
  • 如果只有一个参数,参数类型可以省略,同时()也可以省略。
  • 如果Lambda表达式的方法体代码只有一行代码。可以省略大括号不写,同时要省略分号!
  • 如果Lambda表达式的方法体代码只有一行代码。可以省略大括号不写。此时,如果这行代码是return语句,必须省略return不写,同时也必须省略”;“不写。
package com.liu.day04.LambdaDemo;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
import java.util.Comparator;
public class LambdaDemo3 {
public static void main(String[] args) {
JFrame win = new JFrame("登录界面");
JButton btn = new JButton("按钮");
//最简Lambda表达式
btn.addActionListener( e -> System.out.println("点一下") );
win.add(btn);
win.setSize(400, 300);
win.setVisible(true);
}
}

学习记录5


1 集合

1.1 集合与数组的对比

**相同点:**集合和数组都是容器。

不同点:

对于数组来说:

  • 数组定义完成并启动后,类型确定长度固定

  • 在进行增删数据操作的时候,数组是不太合适的,增删数据都需要放弃原有数组或者移位。

  • 当业务数据的个数是固定的,且都是同一批数据类型的时候,可以采取定义数组存储。

对于集合来说:

  • 集合的大小不固定,启动后可以动态变化类型也可以选择不固定。集合更像气球。
  • 集合非常适合做元素的增删操作。
  • 集合中只能存储引用类型数据,如果要存储基本类型数据可以选用包装类。
  • 当数据的个数不确定,需要进行增删元素的时候,一般使用集合来存储。

1.2 集合类的体系结构

集合主要分为两类:

  1. Collection单列集合,每个元素(数据)只包含一个值。
  2. Map双列集合,每个元素包含两个值(键值对)。

2 Collection单列集合

2.1 Collection集合体系

image-20220727181924132

2.2 Collection集合特点

  • List系列集合:添加的元素是有序、可重复、有索引。 ArrayList,LinekdList:有序、可重复、有索引

  • Set系列集合:添加的元素是无序、不重复、无索引。 HashSet:无序、不重复、无索引;LinkedHashSet: 有序、不重复、无索引。 TreeSet:按照大小默认升序排序、不重复、无索引

  • 集合都是支持泛型的,可以在编译阶段约束集合只能操作某种数据类型。

package com.liu.day05.Collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
public class CollectionDemo01 {
public static void main(String[] args) {
//典型的多态写法 使用左边父类的方法。有序、可重复、有索引
Collection list = new ArrayList<>();
list.add("zhenxi");
list.add(23);
list.add("珍惜大学");
list.add("计算机与控制工程学院");
list.add(true);
list.add(177.1);
System.out.println(list);
//无序、不重复、无索引
Collection set = new HashSet<>();
set.add("zhenxi");
set.add("zhenxi");
set.add(123);
set.add(false);
System.out.println(set);
//对泛型的支持
Collection<Integer> list1 = new ArrayList<>();
list1.add(11);
list1.add(22);
//报错:不兼容的类型: java.lang.String无法转换为java.lang.Integer
//list1.add("dd");
System.out.println(list1);
}
}

2.3 Collection集合常用API

image-20220727185742840
package com.liu.day05.Collection;
import java.util.ArrayList;
import java.util.Collection;
public class CollectionDemo02 {
public static void main(String[] args) {
//Collection常用API
Collection<String> collection = new ArrayList<>();
//add方法
collection.add("zhenxi");
collection.add("珍惜大学");
System.out.println(collection);
//remove方法
collection.remove("zhenxi");
System.out.println(collection);
//contains包含方法
System.out.println(collection.contains("珍惜大学"));
//isEmpty方法
System.out.println(collection.isEmpty());
//size方法
System.out.println(collection.size());
//toArray方法
Object[] array = collection.toArray();
System.out.println(array);
for (Object o : array) {
System.out.println(o);
}
//clear方法
collection.clear();
System.out.println(collection.isEmpty());
}
}

2.4 Collection集合遍历方式

**遍历方式一:**迭代器方式

  • 遍历就是一个一个的把容器中的元素访问一遍。
  • 迭代器在Java中的代表是Iterator,迭代器是集合的专用遍历方式。
image-20220727190951298
package com.liu.day05.Collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionDemo3 {
public static void main(String[] args) {
//遍历方式一:迭代器遍历
Collection<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
System.out.println(list);
//获取迭代器对象
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()){
//判断是否有元素存在,存在继续循环
Integer next = iterator.next();
System.out.println(next); // 自动拆箱
}
}
}

**遍历方式二:**增强for循环

  • 增强for循环:既可以遍历集合也可以遍历数组。
  • 它是JDK5之后出现的,其内部原理是一个lterator迭代器 ,遍历集合相当于是迭代器的简化写法。
package com.liu.day05.Collection;
import java.util.ArrayList;
import java.util.Collection;
public class CollectionDemo04 {
public static void main(String[] args) {
//遍历方式二:For each遍历
Collection<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
System.out.println(list);
//for each
for (Integer integer : list) {
System.out.println(integer);
}
}
}

**遍历方式三:**Lambda表达式

image-20220727193438547
package com.liu.day05.Collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Consumer;
public class CollectionDemo04 {
public static void main(String[] args) {
//遍历方式三:lambda遍历
Collection<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
System.out.println(list);
//lambda表达式
list.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println(integer);
}
});
//简化
list.forEach(integer -> System.out.println(integer));
}
}

2.5 Collection集合存储自定义类型

自定义学生类:

package com.liu.day05.Collection;
public class Student {
private String name;
private int id;
public Student() {
}
public Student(String name, int id) {
this.name = name;
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", id=" + id +
'}';
}
}

集合存储并遍历:

package com.liu.day05.Collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionDemo05 {
public static void main(String[] args) {
//存储自定义类型
Collection<Student> list = new ArrayList<>();
list.add(new Student("zhenxi",2022956775));
list.add(new Student("海珠",2022956776));
list.add(new Student("贾哥",2022956777));
System.out.println(list);
//对自定义类型进行遍历
for (Student student : list) {
System.out.println(student);
}
Iterator<Student> iterator = list.iterator();
while (iterator.hasNext()){
Student student = iterator.next();
System.out.println(student);
}
list.forEach(student -> System.out.println(student));
}
}

3 List集合

3.1 List集合特点

  • ArrayList、LinekdList:有序,可重复,有索引。
  • 有序:存储和取出的元素顺序一致。
  • 有索引:可以通过索引操作元素。
  • 可重复:存储的元素可以重复。

3.2 List底层原理

  • ArrayList底层是基于数组实现的:根据索引定位元素快,增删相对慢。
  • LinkedList底层基于双链表实现的:查询元素慢,增删首尾元素是非常快的。

3.3 List集合的API

1、List集合继承了Collection集合的全部功能,“同时因为List系列集合有索引”。

2、因为List集合多了索引,所以多了很多按照索引操作元素的功能。

3、ArrayList实现类集合底层基于数组存储数据的,查询快,增删慢。

  • public void add(int index, E element): 将指定的元素,添加到该集合中的指定位置上。
  • public E get(int index):返回集合中指定位置的元素。
  • public E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素。
  • public E set(int index, E element):用指定元素替换集合中指定位置的元素,返回更新前的元素值。
package com.liu.day05.Collection;
import java.util.ArrayList;
import java.util.List;
public class ListDemo01 {
public static void main(String[] args) {
// 1.创建一个ArrayList集合对象:
// List:有序,可重复,有索引的。
List<String> list = new ArrayList<>(); // 一行经典代码!
list.add("zhenxi");
list.add("珍惜大学");
list.add("计算机与控制工程学院");
list.add("2022");
list.add("电子信息");
// 2.在某个索引位置插入元素。
list.add(2, "2022967775");
System.out.println(list);
// 3.根据索引删除元素,返回被删除元素
System.out.println(list.remove(1));
System.out.println(list);
// 4.根据索引获取元素:public E get(int index):返回集合中指定位置的元素。
System.out.println(list.get(1));
// 5.修改索引位置处的元素: public E set(int index, E element)
System.out.println(list.set(0, "努力!"));
System.out.println(list);
}
}
/**
运行结果:
[zhenxi, 珍惜大学, 2022967775, 计算机与控制工程学院, 2022, 电子信息]
珍惜大学
[zhenxi, 2022967775, 计算机与控制工程学院, 2022, 电子信息]
2022967775
zhenxi
[努力!, 2022967775, 计算机与控制工程学院, 2022, 电子信息]
Process finished with exit code 0
*/

3.4 List集合遍历方式

(1)for循环。(独有的,因为List有索引)。 (2)迭代器。 (3)foreach。 (4)Lambda表达式

package com.liu.day05.Collection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class ListDemo02 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("zhenxi");
list.add("珍惜大学");
list.add("计算机与控制工程学院");
list.add("2022");
list.add("电子信息");
//for循环
for (int i = 0; i < list.size(); i++) {
String s = list.get(i);
System.out.println(s);
}
//迭代器方式
Iterator<String> it = list.iterator();
while (it.hasNext()){
String ele = it.next();
System.out.println(ele);
}
//for each
for (String ele : list) {
System.out.println(ele);
}
//Lambda表达式
list.forEach(s -> {
System.out.println(s);
});
}
}

3.5 LinkedList集合特有功能

  • 底层数据结构是双链表,查询慢,首尾操作的速度是极快的,所以多了很多首尾操作的特有API。
image-20220727210301320

案例:使用LinkedList模仿栈和队列

package com.liu.day05.Collection;
import java.util.LinkedList;
import java.util.List;
public class ListDemo03 {
public static void main(String[] args) {
// LinkedList可以完成队列结构,和栈结构 (双链表)
// 1、做一个队列:
LinkedList<String> queue = new LinkedList<>();
// 入队
queue.addLast("1号");
queue.addLast("2号");
queue.addLast("3号");
System.out.println(queue);
// 出队
System.out.println(queue.getFirst());
System.out.println(queue.removeFirst());
System.out.println(queue.removeFirst());
System.out.println(queue);
// 2、做一个栈
LinkedList<String> stack = new LinkedList<>();
// 入栈 压栈 (push)
stack.push("第1颗子弹");
stack.push("第2颗子弹");
stack.push("第3颗子弹");
stack.push("第4颗子弹");
System.out.println(stack);
// 出栈 弹栈 pop
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack);
}
}

3.6 List集合修改异常问题

  • 迭代器遍历集合且直接用集合删除元素的时候可能出现。
  • 增强for循环遍历集合且直接用集合删除元素的时候可能出现。
package com.liu.day05.Collection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class ListDemo04 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("zhenxi");
list.add("珍惜大学");
list.add("计算机与控制工程学院");
list.add("2022");
list.add("电子信息");
//迭代器遍历集合且直接用集合删除元素的时候可能出现。
Iterator<String> it = list.iterator();
while (it.hasNext()){
String ele = it.next();
if ("zhenxi".equals(ele)){
//报错ConcurrentModificationException 并发修改异常
//list.remove(ele);
it.remove(); //要用迭代器的删除方法
}
}
System.out.println(list);
//for each
for (String ele : list) {
//报错ConcurrentModificationException 并发修改异常
//list.remove(ele);
//该问题无法解决,因为foreach调用的是迭代器,但是无法直接调用迭代器的remove方法。
System.out.println(ele);
}
}
}

4 泛型

4.1 泛型概述与特点

  • 泛型:是JDK5中引入的特性,可以在编译阶段约束操作的数据类型,并进行检查。
  • 泛型的格式: <数据类型>;注意:泛型只能支持引用数据类型。
  • 集合体系的全部接口和实现类都是支持泛型的使用的。
  • 统一数据类型。
  • 把运行时期的问题提前到了编译期间,避免了强制类型转换可能出现的异常,因为编译阶段类型就能确定下来。

4.2 自定义泛型类

  • 定义类时同时定义了泛型的类就是泛型类。

  • 泛型类的格式:修饰符class类名<泛型变量>{ }。

  • 此处泛型变量T可以随便写为任意标识,常见的如E、T、K、V等。

  • 作用:编译阶段可以指定数据类型,类似于集合的作用。

需求:模拟ArrayList定义一个MyArrayList ,关注泛型设计

package com.liu.day05.genericity;
public class Test {
public static void main(String[] args) {
MyArrayList<String> list = new MyArrayList<>();
list.add("zhenxi");
list.add("珍惜大学");
list.add("计算机与控制工程学院");
list.add("2022");
list.add("电子信息");
System.out.println(list);
MyArrayList<Integer> list2 = new MyArrayList<>();
list2.add(1);
list2.add(2);
list2.add(3);
list2.remove(4);
System.out.println(list2);
}
}
package com.liu.day05.genericity;
import java.util.ArrayList;
public class MyArrayList<E> {
private ArrayList lists = new ArrayList();
public void add(E e){
lists.add(