JAVA学习之知识补充(中)

三:常用类与基础APL:

3.1.String类:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

注意:
1.常量池不可以存放两个相同的String类,也就是说:
String srt1 = "hello";
String str2 = "hello";
实际上它们是可以划等号的,指向相同的地址。
2.基于1,又有不可变性,因此:
String srt1 = "hello";
String str2 = "hello";
str2 += "a";
String str3 = str1.replace('l','w');
str1还是"hello",只不过str2,str3new了一块新地址。

3.2.String实例化的两种方式:

String str1 = "abc";
String str2 = new String("abc");
对于这两种方式,有:

在这里插入图片描述

那么它们的区别在哪里:

在这里插入图片描述

这就是两者的区别,所以S1与S3不一样,而S3,S4不是直接储存在字符串常量池的,所以可以有两个不同的地址,所以也不一样。
同理,对于类的String变量,内存实际上是这么分配的:

在这里插入图片描述

例题分析:

在这里插入图片描述

在这里插入图片描述

解释:

在这里插入图片描述

3.3.String的构造器与转换:

String与char数组:
//String与char数组的互化:

String str = "Hello";
char[] charArray = str.toCharArray(); // 将String转换为char数组

char[] charArray = {'H', 'e', 'l', 'l', 'o'};
String newStr = new String(charArray); // 将char数组转换为String
String与byte数组的互化:
String与byte数组的互化以及使用不同的字符集:
使用默认字符集进行转换:
String str = "Hello";
byte[] byteArray = str.getBytes(); // 将String转换为byte数组
String newStr = new String(byteArray); // 将byte数组转换为String
使用指定的字符集进行转换:
String str = "你好";
byte[] byteArray = str.getBytes("GBK"); // 将String转换为byte数组,使用GBK字符集
String newStr = new String(byteArray, "GBK"); // 将byte数组转换为String,使用GBK字符集
补充:
1.字符集:

UTF-8和GBK是两种常见的字符编码方式,它们的主要区别如下:

字符集覆盖范围:GBK是专门用来解决中文编码的,包含全部中文字符。而UTF-8则包含全世界所有国家需要用到的字符。

编码字节长度:GBK是双字节编码,不论中英文都是双字节。而UTF-8是一种变长的编码方式,它对英文使用8位(即一个字节),中文使用24位(三个字节)来编码。

适用性:如果你主要做中文程序的开发,客户也主要是中国人的话就用GBK吧,因为UTF-8编码的中文使用了三个字节,用GBK节省了空间。如果做英文网站开发,还是用utf-8吧,因为utf-8中英文只占一个字节。GBK中英文也是两个字节的,并且国外客户访问GBK要下载语言包。如果你的网站是中文的,但国外用户也不少,最好也用UTF-8的吧。

2.编码与解码:
String与byte数组互化时,要使用一样的字符集,不然会出错,其次,在byte转化成String时,可以不显示的提供字符集参数:String newStr = new String(byteArray); ,一般会默认为编码时的字符集。
3.构造器:
public String() :初始化新创建的 String 对象,以使其表示空字符序列。
• String(String original): 初始化一个新创建的 String 对象,使其表示一个
与参数相同的字符序列;换句话说,新创建的字符串是该参数字符串的副本。
• public String(char[] value) :通过当前参数中的字符数组来构造新的
String。
• public String(char[] value,int offset, int count) :通过字符数组的
一部分来构造新的 String。
• public String(byte[] bytes) :通过使用平台的默认字符集解码当前参数中的
字节数组来构造新的 String。
• public String(byte[] bytes,String charsetName) :通过使用指定的字符
集解码当前参数中的字节数组来构造新的 String

3.4.常用类:

3.4.1.基础方法:

在这里插入图片描述

3.4.2.查找方法:
11boolean contains(xx):是否包含 xx 。
(12int indexOf(xx):从前往后找当前字符串中 xx,即如果有返回第一次出现的下标,要是没有返回-113int indexOf(String str, int fromIndex):返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始搜索。
(14int lastIndexOf(xx):从后往前找当前字符串中 xx,即如果有返回最后一次出现的下标,要是没有返回-115int lastIndexOf(String str, int fromIndex):返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索。
3.4.3.截取方法:
16String substring(int beginIndex) :返回一个新的字符串,它是此字符串的从 beginIndex开始截取到最后的一个子字符串。 
(17String substring(int beginIndex, int endIndex) :返回一个新字符串,它是此字符串从 beginIndex 开始截取到 endIndex(不包含)的一个子字符串。
3.4.4.和字符/字符数组相关:
18char charAt(index):返回[index]位置的字符 
(19char[] toCharArray(): 将此字符串转换为一个新的字符数组返回 
(20static String valueOf(char[] data) :返回指定数组中表示该字符序列的 String21static String valueOf(char[] data, int offset, int count) : 返回指定数组中表示该字符序列的 String22static String copyValueOf(char[] data): 返回指定数组中表示该字符序列的 String23static String copyValueOf(char[] data, int offset, int count):返回指定数组中表示该字符序列的 String
3.4.5.开头与结尾
24boolean startsWith(xx):测试此字符串是否以指定的前缀开始 
(25boolean startsWith(String prefix, int toffset):测试此字符串从指定索引开始的
子字符串是否以指定前缀开始 
(26boolean endsWith(xx):测试此字符串是否以指定的后缀结束
3.4.6.替换:
27String replace(char oldChar, char newChar):返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。 不支持正则。 
(28String replace(CharSequence target, CharSequence replacement):使用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串。 
(29String replaceAll(String regex, String replacement):使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。 
(30String replaceFirst(String regex, String replacement):使用给定的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。
例题:

在这里插入图片描述

注意,如果在方法内部不改变str的值,那么方法中的str与外面的str其实指向的是同一块地址,但是String有不可变性,所以重新分配了内存,而ch数组就没有这样的规定。

3.5.String,Stringbuffer与StringBuilder:

区分 String、StringBuffer、StringBuilder
String:不可变的字符序列; 底层使用 char[]数组存储(JDK8.0)StringBuffer:可变的字符序列;线程安全(方法有 synchronized 修饰),效率低;底层使用 char[]数组存储 (JDK8.0)StringBuilder:可变的字符序列; jdk1.5 引入,线程不安全的,效率高;底层使用 char[]数组存储(JDK8.0)
因为在底层的源码中,StringBuffer与StringBuilder没有像String一样的finally修饰,所以可在原有的基础上改变。但是,它们在new对象时就将StringBuffer或者StringBuilder的长度定好为16,但是用length方法得到的还是实际的长度,我们在后面可以对它扩容,每次会扩大到原来的2倍加2,但是为了效率更高,我们可以在开始就指定它的大小:
StringBuffer sb = new StringBuffer(10);

常用APL:

1StringBuffer append(xx):提供了很多的 append()方法,用于进行字符串追加的方式拼接 (2StringBuffer delete(int start, int end):删除[start,end)之间字符 
(3StringBuffer deleteCharAt(int index):删除[index]位置字符 
(4StringBuffer replace(int start, int end, String str):替换[start,end)范围的字符序列为 str 
(5void setCharAt(int index, char c):替换[index]位置字符 
(6char charAt(int index):查找指定 index 位置上的字符 
(7StringBuffer insert(int index, xx):在[index]位置插入 xx (8int length():返回存储的字符数据的长度 
(9StringBuffer reverse():反转

其他APL:

1int indexOf(String str):在当前字符序列中查询 str 的第一次出现下标 
(2int indexOf(String str, int fromIndex):在当前字符序列[fromIndex,最后]中查询 str 的第一次出现下标 
(3int lastIndexOf(String str):在当前字符序列中查询 str 的最后一次出现下标 
(4int lastIndexOf(String str, int fromIndex):在当前字符序列[fromIndex,最后]中查询 str 的最后一次出现下标 
(5String substring(int start):截取当前字符序列[start,最后]6String substring(int start, int end):截取当前字符序列[start,end)7String toString():返回此序列中数据的字符串表示形式 
(8void setLength(int newLength) :设置当前字符序列长度为 newLength
三者的效率为:
StringBuilder>StringBuffer>String

例题:

在这里插入图片描述

y=x,让方法中的y指向了x的地址,所以外面的y不变。

在这里插入图片描述

注意,如果为null,那么会把这四个字符存进去。

3.6.日期与时间:

在这里插入图片描述

Date类有两种包下的用法:

3.6.1.Java.util.Date:

3.6.2.Java.sql.Date:

在这里插入图片描述

在Java中,java.sql.Datejava.util.Date之间的转换如下:

import java.util.Date;
import java.sql.Date as SqlDate;

public class Main {
    public static void main(String[] args) {
        // java.util.Date 转 java.sql.Date
        Date utilDate = new Date();
        SqlDate sqlDate = new SqlDate(utilDate.getTime());
        System.out.println("java.util.Date to java.sql.Date: " + sqlDate);

        // java.sql.Date 转 java.util.Date
        sqlDate = SqlDate.valueOf("2024-02-06");
        utilDate = new Date(sqlDate.getTime());
        System.out.println("java.sql.Date to java.util.Date: " + utilDate);
    }
}

在这个例子中,我们首先创建了一个java.util.Date对象,然后使用getTime()方法获取其毫秒值,然后使用这个毫秒值创建了一个java.sql.Date对象。

然后,我们创建了一个java.sql.Date对象,然后使用getTime()方法获取其毫秒值,然后使用这个毫秒值创建了一个java.util.Date对象。

3.6.3.SimpleDateFormat:

在Java中,SimpleDateFormat类是一个非常方便的类,用于格式化和解析日期。以下是一个例子,展示了如何使用SimpleDateFormatDate对象转换为字符串,以及如何将字符串解析为Date对象:
import java.text.SimpleDateFormat;
import java.util.Date;

public class Main {
    public static void main(String[] args) throws Exception {
        // 创建一个Date对象
        Date date = new Date();

        // 创建一个SimpleDateFormat对象
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        // 使用format方法将Date对象转换为字符串
        String dateString = sdf.format(date);
        System.out.println("Formatted date: " + dateString);

        // 使用parse方法将字符串解析为Date对象
        Date parsedDate = sdf.parse(dateString);
        System.out.println("Parsed date: " + parsedDate);
    }
}
这个例子中,我们首先创建了一个Date对象,然后创建了一个SimpleDateFormat对象,并指定了日期和时间的格式。然后,我们使用format方法将Date对象转换为字符串。最后,我们使用parse方法将字符串解析回Date对象。
注意,yyyy-MM-dd HH:mm:ss为指定的格式,我们也可以用空参构造器,即使用默认的格式,但是,我们用parse时,要与最开始的格式一致,prase即把字符串变成Date类型,如果new对象时的指定格式为yyyy-MM-dd HH:mm:ss,那么prase传入的字符串必须也是这种格式。

3.6.4.Calendar:

Java的Calendar类是一个抽象类,它提供了一些方法来操作日期。以下是一些基本的用法:
import java.util.Calendar;

public class Main {
    public static void main(String[] args) {
        // 获取当前日期和时间
        Calendar calendar = Calendar.getInstance();

        // 获取年、月、日、小时、分钟
        int year = calendar.get(Calendar.YEAR);
        int month = calendar.get(Calendar.MONTH);  // 注意:月份是从0开始的,所以实际月份是这里的值+1
        int day = calendar.get(Calendar.DAY_OF_MONTH);
        int hour = calendar.get(Calendar.HOUR_OF_DAY);
        int minute = calendar.get(Calendar.MINUTE);

        System.out.println("当前时间: " + year + "-" + (month + 1) + "-" + day + " " + hour + ":" + minute);

        // 修改日期和时间
        calendar.set(Calendar.YEAR, 2025);
        calendar.set(Calendar.MONTH, Calendar.FEBRUARY);
        calendar.set(Calendar.DAY_OF_MONTH, 25);
        calendar.set(Calendar.HOUR_OF_DAY, 10);
        calendar.set(Calendar.MINUTE, 20);

        year = calendar.get(Calendar.YEAR);
        month = calendar.get(Calendar.MONTH);
        day = calendar.get(Calendar.DAY_OF_MONTH);
        hour = calendar.get(Calendar.HOUR_OF_DAY);
        minute = calendar.get(Calendar.MINUTE);

        System.out.println("修改后的时间: " + year + "-" + (month + 1) + "-" + day + " " + hour + ":" + minute);
    }
}
这个例子中,我们首先使用 Calendar.getInstance() 获取了一个表示当前日期和时间的 Calendar 对象。然后,我们使用 get 方法获取了年、月、日、小时和分钟。注意,月份是从0开始的,所以实际月份是这里的值+1。
然后,我们使用 set 方法修改了年、月、日、小时和分钟。最后,我们再次使用 get 方法获取了修改后的年、月、日、小时和分钟。
注意,在Java中,Calendar是一个抽象类,不能直接实例化。

3.6.5.JDK8的新的日期与时间:

上面两种方法可能遇到的问题:

• 可变性:像日期和时间这样的类应该是不可变的。
• 偏移性:Date 中的年份是从 1900 开始的,而月份都从 0 开始。
• 格式化:格式化只对 Date 有用,Calendar 则不行。
• 此外,它们也不是线程安全的;不能处理闰秒等。

java.time包是Java 8引入的新的日期和时间API,它修复了旧的java.util.Datejava.util.Calendar的许多问题,并提供了更强大、更灵活的日期和时间处理功能。以下是一些主要的类:

  • LocalDate:表示日期(年、月、日)。
  • LocalTime:表示时间(小时、分钟、秒)。
  • LocalDateTime:表示日期和时间。
  • Instant:表示一个时间戳,可以用来记录事件发生的瞬间。
  • Period:表示两个日期之间的时间段。
  • Duration:表示两个时间之间的时间段。
以下是一些使用示例:
import java.time.*;

public class Main {
    public static void main(String[] args) {
        // 获取当前日期和时间
        LocalDate date = LocalDate.now();
        LocalTime time = LocalTime.now();
        LocalDateTime dateTime = LocalDateTime.now();
        System.out.println("当前日期: " + date);
        System.out.println("当前时间: " + time);
        System.out.println("当前日期和时间: " + dateTime);

        // 创建日期和时间
        LocalDate dateOfBirth = LocalDate.of(1990, Month.JANUARY, 1);
        LocalTime teaTime = LocalTime.of(17, 0);
        LocalDateTime dateTimeOfBirth = LocalDateTime.of(dateOfBirth, teaTime);
        System.out.println("出生日期: " + dateOfBirth);
        System.out.println("茶时间: " + teaTime);
        System.out.println("出生日期和时间: " + dateTimeOfBirth);

        // 计算时间段
        Period period = Period.between(dateOfBirth, date);
        System.out.println("从出生到现在的天数: " + period.getDays());
        Duration duration = Duration.between(teaTime, time);
        System.out.println("从茶时间到现在的秒数: " + duration.getSeconds());
    }
}
now()/ now(ZoneId zone) 静态方法,根据当前时间创建对象/指定时区的对象
of(xx,xx,xx,xx,xx,xxx) 静态方法,根据指定日期/时间创建对象
getDayOfMonth()/getDayOfYear() 获得月份天数(1-31) /获得年份天数(1-366)
getDayOfWeek() 获得星期几(返回一个 DayOfWeek 枚举值)
getMonth() 获得月份, 返回一个 Month 枚举值
getMonthValue() / getYear() 获得月份(1-12) /获得年份
getHours()/getMinute()/getSecond() 获得当前对象对应的小时、分钟、秒withDayOfMonth()/withDayOfYear()/withMonth()/withYear()将月份天数、年份天数、月份、年份修改为指定的值并返回新的对象
with(TemporalAdjuster t) 将当前日期时间设置为校对器指定的日期时间
plusDays(), plusWeeks(),plusMonths(),plusYears(),plusHours()向当前对象添加几天、几周、几月、几年、几小时
minusMonths() /minusWeeks()/minusDays()/minusYears()/minusHours()从当前对象减去几月、几周、几天、几年、几小时
plus(TemporalAmountt)/minus(TemporalAmount t)添加或减少一个 DurationPeriod
isBefore()/isAfter() 比较两个 LocalDate
isLeapYear() 判断是否是闰年(在 LocalDate 类中声明)
format(DateTimeFormatter t) 格式化本地日期、时间,返回一个字符串
parse(Charsequence text) 将指定格式的字符串解析为日期、时间

3.7.Java比较器:

3.7.1.Comparable:

在这里插入图片描述

package com.atguigu.api;

import java.util.Arrays;

public class Student implements Comparable<Student> {
    private int id;
    private String name;
    private int score;
    private int age;

    public Student(int id, String name, int score, int age) {
        this.id = id;
        this.name = name;
        this.score = score;
        this.age = age;
    }

    // ... 其他getter和setter方法 ...

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", score=" + score +
                ", age=" + age +
                '}';
    }

    @Override
    public int compareTo(Student other) {
        return this.id - other.id;
    }
}

public class TestStudent {
    public static void main(String[] args) {
        Student[] students = new Student[5];
        students[0] = new Student(3, "张三", 90, 23);
        students[1] = new Student(1, "熊大", 100, 22);
        students[2] = new Student(5, "王五", 75, 25);
        students[3] = new Student(4, "李四", 85, 24);
        students[4] = new Student(2, "熊二", 85, 18);

		//单独比较两个对象
		System.out.println(arr[0].compareTo(arr[1]));
		
        System.out.println("所有学生:");
        for (Student student : students) {
            System.out.println(student);
        }

        System.out.println("按照学号排序:");
        Arrays.sort(students);
        for (Student student : students) {
            System.out.println(student);
        }
    }
}

3.7.2.Comparator:

在这里插入图片描述

在这里插入图片描述

举例:

package com.atguigu.api;

import java.util.Comparator;

// 定义定制比较器类
public class StudentScoreComparator implements Comparator {
    @Override
    public int compare(Object o1, Object o2) {
        if (!(o1 instanceof Student) || !(o2 instanceof Student)) {
            throw new IllegalArgumentException("Objects must be of type Student");
        }
        Student s1 = (Student) o1;
        Student s2 = (Student) o2;
        int result = s1.getScore() - s2.getScore();
        return result != 0 ? result : s1.getId() - s2.getId();
    }
}

// 测试类
package com.atguigu.api;

public class TestStudent {
    public static void main(String[] args) {
        Student[] arr = new Student[5];
        arr[0] = new Student(3, "张三", 90, 23);
        arr[1] = new Student(1, "熊大", 100, 22);
        arr[2] = new Student(5, "王五", 75, 25);
        arr[3] = new Student(4, "李四", 85, 24);
        arr[4] = new Student(2, "熊二", 85, 18);

        System.out.println("所有学生:");
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }

        System.out.println("按照成绩排序");
        StudentScoreComparator sc = new StudentScoreComparator();
        for (int i = 1; i < arr.length; i++) {
            for (int j = 0; j < arr.length - i; j++) {
                if (sc.compare(arr[j], arr[j + 1]) > 0) {
                    Student temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }

        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
    }
}
这是直接造了一个类的方法。当然,我们还可以
Arrays.sort(all, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
Goods g1 = (Goods) o1;
Goods g2 = (Goods) o2;
return g1.getName().compareTo(g2.getName());
}
});
用匿名对象的方法进行重写。
注意,如果要反序,则在return后面加上负号即可。

3.8.其他类APL:

3.8.1.java.lang.System 类:
java.lang.System 类代表当前 Java 程序的运行平台,系统级的很多属性和控制方法都放置在该类的内部。它包含了一些有用的类字段和方法。以下是一些主要的特性:
类声明:public final class System extends Object。由于该类的构造方法是 private 的,所以无法创建该类的对象,也就是无法实例化该类。其内部的成员方法和成员变量都是 static(静态)的,所以也可以很方便地调用他。
字段:包括 static PrintStream err(标准错误输出流)、static InputStream in(标准输入流)和 static PrintStream out(标准输出流)。
类方法:包括但不限于 arraycopy()(从指定源数组中复制一个数组)、clearProperty()(删除指定键指示的系统属性)、console()(返回与当前 Java 虚拟机关联的唯一 Console 对象)、currentTimeMillis()(以毫秒为单位返回当前时间)、exit()(终止当前正在运行的 Java 虚拟机)、gc()(运行垃圾收集器)、getenv()(返回当前系统环境的不可修改的字符串映射视图)等。
3.8.2.java.lang.Runtime 类:
这个类允许应用程序与运行应用程序的环境进行交互。它提供了一系列的方法,包括执行系统命令、获取可用处理器数量、运行垃圾收集器等。这个类是单例的,可以通过 Runtime.getRuntime() 方法获取当前运行时对象的引用。
3.8.3.java.lang.Math类:
这个类包含用于执行基本数值运算的方法,例如初等指数、对数、平方根和三角函数。所有的这些方法都是静态的,可以直接通过类名调用。
3.8.4.java.math 包:
这个包提供了用于执行任意精度整数算术 (BigInteger) 和任意精度十进制算术 (BigDecimal) 的类。
3.8.5.java.util.Random类:
这个类用于生成伪随机数。它提供了一系列的方法,可以生成不同类型的随机数,包括整数、浮点数、高斯分布的数等。这个类是线程安全的,但在多线程环境中,建议使用 ThreadLocalRandom 以获得更好的性能。

四:集合框架:

(1):Collection:

在这里插入图片描述

Collection 接口及方法:
• JDK 不提供此接口的任何直接实现,而是提供更具体的子接口(如:Set 和 List)去实现。
• Collection 接口是 List 和 Set 接口的父接口,该接口里定义的方法既可用于操作 Set集合,也可用于操作 List 集合。方法如下:

1.1添加:

1add(E obj):添加元素对象到当前集合中 
(2addAll(Collection other):添加 other 集合中的所有元素对象到当前集合中
举例:
注意:add 和 addAll 的区别
package com.atguigu.collection;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Collection;

public class TestCollectionAdd {
    @Test
    public void testAdd(){
        //ArrayList 是 Collection 的子接口 List 的实现类之一。
        Collection coll = new ArrayList();
        coll.add("小李广");
        coll.add("扫地僧");
        coll.add("石破天");
        System.out.println(coll);
    }

    @Test
    public void testAddAll(){
        Collection c1 = new ArrayList();
        c1.add(1);
        c1.add(2);
        System.out.println("c1集合元素的个数:" + c1.size());//2
        System.out.println("c1 = " + c1);
        
        Collection c2 = new ArrayList();
        c2.add(1);
        c2.add(2);
        System.out.println("c2集合元素的个数:" + c2.size());//2
        System.out.println("c2 = " + c2);
        
        Collection other = new ArrayList();
        other.add(1);
        other.add(2);
        other.add(3);
        System.out.println("other集合元素的个数:" + other.size());//3
        System.out.println("other = " + other);
        System.out.println();
        
        c1.addAll(other);
        System.out.println("c1集合元素的个数:" + c1.size());//5
        System.out.println("c1.addAll(other) = " + c1);
        
        c2.add(other);
        System.out.println("c2集合元素的个数:" + c2.size());//3
        System.out.println("c2.add(other) = " + c2);
    }
}
add也可以添加一个对象,不过是把这个对象当作整体,算成一个元素。

1.2 判断:

3int size():获取当前集合中实际存储的元素个数 
(4boolean isEmpty():判断当前集合是否为空集合 
(5boolean contains(Object obj):判断当前集合中是否存在一个与 obj 对象 equals 返回 true 的元素 
(6boolean containsAll(Collection coll):判断 coll 集合中的元素是否在当前集合中都存在
。即 coll 集合是否是当前集合的“子集” 
(7boolean equals(Object obj):判断当前集合与 obj 是否相等
注意:Collection类有时需要重写equals方法,是因为在使用集合类存储对象时,需要比较对象的内容而不是引用地址。默认的equals方法是比较对象的引用地址,而不是对象的内容。因此,如果需要在集合中存储自定义对象,并且希望能够比较对象的内容而不是引用地址,就需要重写equals方法来实现自定义的对象比较逻辑。这样可以确保集合类能够正确地判断两个对象是否相等,从而保证集合类的功能正常运作。
举例:
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null || getClass() != obj.getClass()) {
            return false;
        }
        Person person = (Person) obj;
        return age == person.age && name.equals(person.name);
    }
}

public class Main {
    public static void main(String[] args) {
        Person person1 = new Person("Alice", 25);
        Person person2 = new Person("Bob", 30);
        Person person3 = new Person("Alice", 25);

        List personList = new ArrayList();
        personList.add(person1);

        // 使用contains方法来检查是否包含某个Person对象
        System.out.println(personList.contains(person2)); // 输出false,因为内容不同
        System.out.println(personList.contains(person1)); // 输出true,因为内容相同

        // 使用containsAll方法来检查是否包含另一个Collection中的所有元素
        List anotherList = new ArrayList();
        anotherList.add(person1);
        anotherList.add(person3);
        System.out.println(personList.containsAll(anotherList)); // 输出true,因为personList包含anotherList中的所有元素

        // 使用equals方法来比较两个Collection是否相等
        List personList2 = new ArrayList();
        personList2.add(person1);
        personList2.add(person2);
        System.out.println(personList.equals(personList2)); // 输出false,因为内容不同
    }
}

1.3 删除:

8void clear():清空集合元素 
(9boolean remove(Object obj) :从当前集合中删除第一个找到的与 obj 对象 equals 返回 true 的元素。 
(10boolean removeAll(Collection coll):从当前集合中删除所有与 coll 集合中相同
的元素。即 this = this - this ∩ coll 
(11boolean retainAll(Collection coll):从当前集合中删除两个集合中不同的元素,使得当前集合仅保留与 coll 集合中的元素相同的元素,即当前集合中仅保留两个集合的交集,即 this = this ∩ coll;

1.4 其它:

12Object[] toArray():返回包含当前集合中所有元素的数组 
(13hashCode():获取集合对象的哈希值 
(14iterator():返回迭代器对象,用于集合遍历
在Java中,可以使用Arrays类的asList()方法将数组转换为List,然后再使用集合的构造函数将List转换为集合。
import java.util.*;

public class ArrayToSet {
    public static void main(String[] args) {
        Integer[] arr = {1, 2, 3, 4, 5};
        List<Integer> list = Arrays.asList(arr);
        Set<Integer> setArr = new HashSet<>(list);
        System.out.println(setArr);
    }
}
输出:
[1, 2, 3, 4, 5]
例题分析:

在这里插入图片描述

迭代器是一种用于遍历集合类的对象,它提供了一种统一的方式来访问集合中的元素。在Java中,可以使用迭代器来遍历Collection类的实例,如List、Set和Map。
下面是一个使用迭代器遍历List集合的示例代码:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("apple");
        list.add("banana");
        list.add("orange");

        // 使用迭代器遍历List集合
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            String element = iterator.next();
            System.out.println(element);
        }
    }
}
在上面的示例中,我们首先创建了一个ArrayList实例,并向其中添加了一些元素。然后,我们使用list.iterator()方法获取了一个迭代器对象,然后使用while循环和iterator.hasNext()方法来遍历集合中的元素,使用iterator.next()方法获取每个元素的值并进行处理。
类似地,我们也可以使用迭代器来遍历其他类型的集合,如Set和Map。迭代器提供了一种通用的方式来遍历集合中的元素,可以帮助我们更方便地对集合进行操作。

1.5.增强for循环:

增强for循环是一种简化迭代集合或数组的方式,它可以更简洁地遍历集合中的元素。在Java中,增强for循环的语法如下:
for (元素类型 元素变量 : 集合或数组) {
    // 循环体
}
其中,元素类型是集合或数组中元素的类型,元素变量是用来存储每个元素的变量名,集合或数组是要遍历的对象。
例如,遍历一个整型数组可以这样写:
int[] nums = {1, 2, 3, 4, 5};
for (int num : nums) {
    System.out.println(num);
}
这段代码会依次输出数组中的每个元素。
增强for循环不仅可以遍历数组,还可以遍历集合类,如ArrayList、LinkedList等。它能够简化代码,提高代码的可读性,并且减少了迭代器的使用,使得代码更加简洁和易于理解。
注意,在内部改变临时变量的值,可能不影响集合中的值。

(2):List:

2.1.常用方法:

• 插入元素
– void add(int index, Object ele):在 index 位置插入 ele 元素
– boolean addAll(int index, Collection eles):从 index 位置开始将 eles 中的所有元素添加进来
• 获取元素
– Object get(int index):获取指定 index 位置的元素
– List subList(int fromIndex, int toIndex):返回从 fromIndex 到 toIndex 位置的子集合
• 获取元素索引
– int indexOf(Object obj):返回 obj 在集合中首次出现的位置
– int lastIndexOf(Object obj):返回 obj 在当前集合中末次出现的位置
• 删除和替换元素
– Object remove(int index):移除指定 index 位置的元素,并返回此元素
– Object set(int index, Object ele):设置指定 index 位置的元素为ele

2.2.实现类:

在这里插入图片描述

(3):set:

3.1.基础知识:

在这里插入图片描述

在这里插入图片描述

也就是说,即使是LinkedHashSet,也是无序储存的,只不过在储存时记录了前后节点,所以在一定程度上保留了顺序。
因为在判断时,除了equals方法,还有hashcode所以,我们:

在这里插入图片描述

3.2.Treeset:

在这里插入图片描述

比如,两个对象的age相同,我们自定义的比较方法认为它们相同,那么它们无法同时存在。

TreeSet 两种排序方法:

自然排序和定制排序。默认情况下,TreeSet 采用自然排序。

自然排序:TreeSet 会调用集合元素的 compareTo(Object obj) 方法来比较元素之间的大小关系,然后将集合元素按升序(默认情况)排列。
• 如果试图把一个对象添加到 TreeSet 时,则该对象的类必须实现Comparable 接口。
• 实现 Comparable 的类必须实现 compareTo(Object obj) 方法,两个对象即通过compareTo(Object obj) 方法的返回值来比较大小。

定制排序:如果元素所属的类没有实现 Comparable 接口,或不希望按照升序(默认情况)的方式排列元素或希望按照其它属性大小进行排序,则考虑使用定制排序。定制排序,通过 Comparator 接口来实现。需要重写compare(T o1,T o2)方法。
• 利用 int compare(T o1,T o2)方法,比较 o1 和 o2 的大小:如果方法返回正整数,则表示 o1 大于 o2;如果返回 0,表示相等;返回负整数,表示 o1 小于 o2。
• 要实现定制排序,需要将实现 Comparator 接口的实例作为形参传递给 TreeSet 的构造器。

举例:

import java.util.*;

class CustomComparator implements Comparator {
    public int compare(Object a, Object b) {
        return ((Integer)b).intValue() - ((Integer)a).intValue();  // 降序排序
    }
}

public class TreeSetExample {
    public static void main(String[] args) {
        // 自然排序
        Set naturalOrderSet = new TreeSet();
        naturalOrderSet.add(new Integer(5));
        naturalOrderSet.add(new Integer(1));
        naturalOrderSet.add(new Integer(3));
        System.out.println("自然排序: " + naturalOrderSet);  // 输出: [1, 3, 5]

        // 定制排序
        Set customOrderSet = new TreeSet(new CustomComparator());
        customOrderSet.add(new Integer(5));
        customOrderSet.add(new Integer(1));
        customOrderSet.add(new Integer(3));
        System.out.println("定制排序: " + customOrderSet);  // 输出: [5, 3, 1]
    }
}
这些代码演示了如何在不使用泛型的情况下,使用TreeSet的两种比较方式。

(4):Map:

4.1.基础知识:

Java中的Map是一种键值对的集合,它存储着一组键值对,并且允许通过键来查找值。Map接口是java.util包中的一部分,它是一个泛型接口,可以用来存储任意类型的键值对。
Map中的键是唯一的,每个键对应着一个值。可以通过键来获取对应的值,也可以通过键来添加、删除或更新键值对。常用的Map实现类包括HashMap、TreeMap和LinkedHashMap。

在这里插入图片描述

注:Hashtable中可以应该更正为不可以。

在这里插入图片描述

4.2.常用方法:

在这里插入图片描述

在这里插入图片描述

在Java中,Map接口中的entrySet()方法可以返回一个包含Map.Entry对象的Set集合,其中每个Map.Entry对象包含一个键值对。可以通过entrySet()方法来遍历Map中的键值对。
  1. 使用迭代器遍历entrySet
Map map = new HashMap();
// 添加键值对
map.put("A", 1);
map.put("B", 2);
map.put("C", 3);

Set entrySet = map.entrySet();
Iterator iterator = entrySet.iterator();
while (iterator.hasNext()) {
    Map.Entry entry = iterator.next();
    String key = entry.getKey();
    Integer value = entry.getValue();
    System.out.println("Key: " + key + ", Value: " + value);
}
2.使用增强for循环遍历entrySet
for (Map.Entry entry : map.entrySet()) {
    String key = entry.getKey();
    Integer value = entry.getValue();
    System.out.println("Key: " + key + ", Value: " + value);
}
3.使用forEach方法遍历entrySet
map.forEach((key, value) -> {
    System.out.println("Key: " + key + ", Value: " + value);
});
以上是Map中entry的多种遍历方法,可以根据实际情况选择适合的遍历方式。

4.3.TreeMap:

TreeMap是Java中的一个基于红黑树的有序映射,它可以根据键的自然顺序或者提供的Comparator进行排序。下面是一个例子,展示了如何在TreeMap中使用自然排序和定制排序:
import java.util.*;

class CustomComparator implements Comparator {
    public int compare(Object a, Object b) {
        return ((String)b).compareTo((String)a);  // 降序排序
    }
}

public class TreeMapExample {
    public static void main(String[] args) {
        // 自然排序
        Map naturalOrderMap = new TreeMap();
        naturalOrderMap.put("Mike", 18);
        naturalOrderMap.put("Joney", 28);
        naturalOrderMap.put("Dan", 14);
        System.out.println("自然排序: " + naturalOrderMap);  // 输出: {Dan=14, Joney=28, Mike=18}

        // 定制排序
        Map customOrderMap = new TreeMap(new CustomComparator());
        customOrderMap.put("Mike", 18);
        customOrderMap.put("Joney", 28);
        customOrderMap.put("Dan", 14);
        System.out.println("定制排序: " + customOrderMap);  // 输出: {Mike=18, Joney=28, Dan=14}
    }

}
在这个例子中,我们首先创建了一个TreeMap,并添加了一些元素。由于我们没有提供自定义的Comparator,所以TreeMap使用键的自然排序。然后,我们创建了另一个TreeMap,并提供了一个自定义的Comparator来实现降序排序。当我们添加元素并打印TreeMap时,我们可以看到元素是按照我们指定的顺序排序的。

4.4.properties:

Properties类是Java中的一个用于处理属性文件的工具类,它继承自Hashtable1。Properties类常用于存储程序的配置信息,例如数据库连接信息、日志输出配置、应用程序设置等1。使用Properties类,可以将这些信息存储在一个文本文件中,并在程序中读取这些信息1。
下面是一个简单的例子,演示了如何使用Properties类来读取和写入属性文件:
import java.io.*;
import java.util.Properties;

public class PropertiesExample {
    public static void main(String[] args) {
        Properties prop = new Properties();
        try {
            // 读取属性文件
            prop.load(new FileInputStream("config.properties"));

            // 获取属性值
            String username = prop.getProperty("username");
            String password = prop.getProperty("password");
    
            // 输出属性值
            System.out.println("username: " + username);
            System.out.println("password: " + password);
    
            // 修改属性值
            prop.setProperty("username", "newUsername");
            prop.setProperty("password", "newPassword");
    
            // 保存属性到文件
            prop.store(new FileOutputStream("config.properties"), null);
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

}
在这个例子中,我们创建了一个Properties对象,然后使用load()方法从配置文件中读取属性。接着,我们使用getProperty()方法获取属性值,并输出到控制台。然后,我们使用setProperty()方法修改属性值,并使用store()方法将修改后的属性保存回文件。

(5):Collections工具类:

在这里插入图片描述

Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象实现同步控制等方法(均为static 方法):
排序操作:
reverse(List):反转 List 中元素的顺序
• shuffle(List):对 List 集合元素进行随机排序
• sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
• sort(ListComparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
• swap(Listintint):将指定 list 集合中的 i 处元素和 j 处元素进行交换
查找:
Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
• Object max(CollectionComparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素
• Object min(Collection):根据元素的自然顺序,返回给定集合中的最小元素
• Object min(CollectionComparator):根据 Comparator 指定的顺序,返回给定集合中的最小元素
• int binarySearch(List list,T key)List 集合中查找某个元素的下标,但是 List 的元素必须是 TT 的子类对象,而且必须是可比较大小的,即支持自然排序的。而且集合也事先必须是有序的,否则结果不确定。
• int binarySearch(List list,T key,Comparator c)List 集合中查找某个元素的下标,但是 List 的元素必须是 TT 的子类对象,而且集合也事先必须是按照 c 比较器规则进行排序过的,否则结果不确定。
• int frequency(Collection c,Object o):返回指定集合中指定元素的出现次数
注意,它默认最右边为最大值。
复制、替换:
void copy(List dest,List src):将 src 中的内容复制到 dest 中
• boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换 List 对象的所有旧值
• 提供了多个 unmodifiableXxx()方法,该方法返回指定 Xxx 的不可修改的视图,即只可以读。
注意,复制时要注意集合的大小关系。
添加:
boolean addAll(Collection c,T... elements)将所有指定元素添加到指定 collection中。

五:泛型:

Java的泛型是一种在编译时进行类型检查和类型安全的机制,它允许我们在定义类、接口和方法时使用一个或多个类型参数。通过使用泛型,我们可以编写更加通用和可复用的代码,同时可以避免在运行时出现类型转换错误。

5.1.泛型类:

在Java中,我们可以使用尖括号(<>)来声明泛型类型参数,例如:
public class Box<T> {
    private T value;

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }
}
在上面的示例中,Box类使用了一个类型参数T,这使得我们可以在创建Box对象时指定具体的类型,例如:
Box<Integer> intBox = new Box<>();
intBox.setValue(10);
int value = intBox.getValue(); // 不需要进行类型转换
使用泛型可以提高代码的可读性和安全性,因为编译器会在编译时进行类型检查,确保我们不会将错误类型的对象放入泛型容器中。此外,泛型还可以减少代码中的重复和冗余,使得代码更加简洁和易于维护。
总的来说,Java的泛型是一种非常有用的特性,它可以帮助我们编写更加灵活和健壮的代码,提高代码的可复用性和可维护性。

我们再来看一个例子:

public class Box<T> {
    private T content;

    public void setContent(T content) {
        this.content = content;
    }

    public T getContent() {
        return content;
    }

    public static void main(String[] args) {
        Box<String> stringBox = new Box<>();
        stringBox.setContent("Hello, World!");
        System.out.println(stringBox.getContent());

        Box<Integer> intBox = new Box<>();
        intBox.setContent(123);
        System.out.println(intBox.getContent());
    }
}
在Box类中,所以的参数类型,以及部分返回值类型,全部都变成了T,而在main方法中,我们定义的类泛型为String,则后面在Box方法中凡是定义为T类型的地方,必须使用泛型。注意:
Box<String> stringBox = new Box<>();
Box<String> stringBox = new Box<String>();
都可以,第一种属于类型推断,是JDK7引入的新特性。

注意,在JDK10时,加入了类似于C++中auto关键字的var,效果相同,可自动推断类型。

在这里插入图片描述

泛型参数在指明时,不可以使用基本数据类型,但是可以使用包装类。
如果不指明泛型的子类是否为泛型,则默认为Object。注意:

class B extends Order<T> 不是泛型类

  • 在这个声明中,B 是一个普通的类,而不是泛型类。它继承自 Order<T>,但并没有指定具体的类型。
  • 如果要使 B 成为泛型类,应该在类名后面添加类型参数,例如:class B<T> extends Order<T>
当我们比较这三个类的声明时,我们需要理解泛型的不同用法。让我详细解释一下这三者之间的区别:
class B<T> extends Order<String>:
这是一个泛型类 B,它继承自泛型类 Order<String>。
在实例化 B 时,类型参数 T 可以被指定为某个适合的类型。
而继承父类的未确定方法的方法都被设置为String类型。
class B<T> extends Order<T>:
这同样是一个泛型类 B,它继承自泛型类 Order<T>T 是一个类型变量,可以代表不同的类型。
在实例化 B 时,你可以选择不同的类型。
class B<T, E> extends Order<String>:
这是一个具有两个类型参数的泛型类 B,它继承自泛型类 Order<String>。
类型参数 TE 可以分别代表不同的类型。
在实例化 B 时,TE 都可以选择不同的类型。


在这里插入图片描述

5.2.泛型方法:

1.泛型方法的定义
public <T> void printArray(T[] array) {
    for (T element : array) {
        System.out.print(element + " ");
    }
    System.out.println();
}
2.调用泛型方法
Integer[] intArray = {1, 2, 3, 4, 5};
String[] strArray = {"Hello", "World"};

printArray(intArray); // 输出: 1 2 3 4 5
printArray(strArray); // 输出: Hello World

在这里插入图片描述

5.3.泛型在继承的使用与通配符:

举例:

注意,这是错误的写法,如果不使用泛型,Object的类可以赋值为String类型的类等,但是如果是泛型,则不行。

在这里插入图片描述

怎么解决这种问题呢,所以就有了通配符:

通配符的使用场景:
  1. 在泛型类或泛型接口中使用通配符可以使类或接口更加灵活,可以接受不同类型的参数。
  2. 在泛型方法中使用通配符可以处理不同类型的参数。

在这里插入图片描述

通配符有三种形式:
  1. ?:表示未知类型,可以用于声明变量或参数。
  2. ? extends T:表示未知类型是T或T的子类。
  3. ? super T:表示未知类型是T或T的父类。

在这里插入图片描述

关于? extends T
此处的extents相当于小于等于符号,意思是进行赋值的时候,可以将小于等于T这一个类型的赋给该对象,因为是小于等于,可以理解为负无穷到T,因为添加的可能是该区间的任何一种类型,所以读取,要用T类型来接收,因为该类型一定大于等于你赋值的那一个类型,而写入,只能写入null,因为该类型可能是位于这个区间的无穷小,因此,无法保证除了null以外的类型可以正确的写进去。
关于? super T
此处的super相当于大于等于符号,意思是进行赋值的时候,可以将大于等于T这一个类型的赋给该对象,因为是大于等于,可以理解为T到Object,因为添加的可能是该区间的任何一种类型,所以读取,要用Object类型来接收,因为该类型一定大于等于你赋值的那一个类型,而写入,可以写入T或者小于T的任何类型,因为该类型可以保证一定小于你设计的那个类型。
  • 25
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Xiao Ling.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值