目录
1.9.3.4.1 equals和==区别,为什么重写equals要重写hashcode
1.9.4.4StringBulider与String的转化
1.9.6String、StringBuilder、StringBuffer的区别
1.9.1API
1.9.1.1什么是API
API(Application Programming Interface),应用程序编程接口。Java API是一本程序员的字典 ,是JDK中提供给我们使用的类的说明文档。这些类将底层的代码实现封装了起来,我们不需要关心这些类是如何实现的,只需要学习这些类如何使用即可。所以我们可以通过查询API的方式,来学习Java提供的类,并得知如何使用它们。
1.9.1.2API如何使用
1. 打开帮助文档。
2. 点击显示,找到索引,看到输入框。
3. 你要找谁?在输入框里输入,然后回车。
4. 看包。java.lang下的类不需要导包,其他需要。
5. 看类的解释和说明。
6. 学习构造方法。
1.9.1.3Scanner类演示使用API
什么是scanner类
一个可以解析基本类型和字符串的简单文本扫描器。 例如,以下代码使用户能够从 System.in 中读取一个数:
Scanner sc = new Scanner(System.in);
int i = sc.nextInt();
1.9.2Object类
java.lang.Object
类是Java语言中的根类,即所有类的父类。
在API中有很多Object类的方法,我们这里主要讲解toString,equal方法
1.9.2.1toString方法
public String toString()
:返回该对象的字符串表示。
toString方法返回该对象的字符串表示,其实该字符串内容就是对象的类型+@+内存地址值。
由于toString方法返回的结果是内存地址,而在开发中,经常需要按照对象的属性得到相应的字符串表现形式,因此也需要重写它。
1.9.2.2equal方法
-
public boolean equals(Object obj)
:指示其他某个对象是否与此对象“相等”。调用成员方法equals并指定参数为另一个对象,则可以判断这两个对象是否是相同的。这里的“相同”有默认和自定义两种方式。
-
默认比较方式
如果没有覆盖重写equals方法,那么Object类中默认进行
==
运算符的对象地址比较,只要不是同一个对象,结果必然为false。 -
重写equals方法
如果希望进行对象的内容比较,即所有或指定的部分成员变量相同就判定两个对象相同,则可以覆盖重写equals方法。
1.9.3String类
1.9.3.1String概述
String
类代表字符串。Java程序中所有的字符串文字(例如 "abc" )都可以被看作是实现此类的实例。
String
类 中包括用于检查各个字符串的方法,比如用于比较字符串,搜索字符串,提取子字符串以及创建具有翻译为大写或小写的所有字符的字符串的副本。
1.9.3.2String类的特点
字符串不变:字符串的值在创建后不能被更改
String s1 = "abc";
s1 += "d"; System.out.println(s1); // "abcd"
// 内存中有"abc","abcd"两个对象,s1从指向"abc",改变指向,指向了"abcd"。
因为String对象是不可变的,所以它们可以被共享。
String s1 = "abc";
String s2 = "abc";
// 内存中只有一个"abc"对象被创建,同时被s1和s2共享。
String的本质是
-
JDK8之前是char[] 数组
-
JDK8之后是byte[] 数组
1.9.3.3String类常用方法
1.9.3.3.1比较
-
public boolean equals (Object anObject)
:将此字符串与指定对象进行比较 -
public boolean equalsIgnoreCase(String anotherString)
: 将此字符串与指定对象进行比较,忽略大小写 -
public boolean endsWith(String suffix)
: 判断此字符串是否以指定的后缀结束 -
public boolean startsWith(String prefix)
: 判断此字符串是否以指定的前缀开始
1.9.3.3.2获取
-
public int length()
:返回此字符串的长度 -
public String concat(String str)
: 将指定的字符串连接到该字符串的末尾。 -
public char charAt(int index)
: 返回指定索引处的char值。 -
public int indexOf(String ch)
:返回指定子字符串第一次出现在该字符串内的索引。 -
public String substring(int beginIndex)
:返回一个子字符串,从beginIndex开始截取字符串到字符串结尾。 -
public String substring(int beginIndex,int endIndex)
:返回一个子字符串,从beginIndex到endIndex截取字符串。含beginIndex,不含endIndex。
1.9.3.3.3转换
-
public char[] toCharArray()
:将此字符串转换为新的字符数组。 -
public byte[] getBytes()
:使用平台的默认字符集将该String编码转换为新的字节数组。 -
public String replace(char oldChar, char newChar)
:将oldChar匹配的字符串使用newChar字符串替换。 -
public String replaceFirst(String regex,String replacement)
用给定的 replacement 替换此字符串匹配给定的regex的第一个子字符串。 -
public String toUpperCase()
: 将字符中转换为大写 -
public String toLowerCase()
: 将字符中转换为小写
1.9.3.3.4分割和去空格
-
public String[] split(String regex)
:将此字符串按照给定的regex(规则)拆分为字符串数组。 -
public String trim()
:去除该字符串的两端空格
1.9.3.4 String类面试问题
1.9.3.4.1 equals和==区别,为什么重写equals要重写hashcode
==是运算符,equals是来自于object类定义的方法,由于本质不一样导致使用范围不一样,==可以用于基本数据类型和引用类型,equals只能用于引用类型。
==两端如果是基本数据类型,就是判断值(地址)是否相同,equals在重写之后,判断两个对象的属性值是否相同。
如果equals不重写,其实就是==号,以下是equals的源码。
重写equals可以让我们自己定义判断两个对象是否相同的条件
hashcode:是一个类对象,区别于其他任意类对象的唯一编码。
不重写的话,object里定义有hashcode定义
native方法,不是java代码实现的,而是由JVM直接算好的。但是,object定义的hashcode方法生成的哈希码能保证同一个类的对象的哈希码一定是不同的,不同类就有可能有相同哈希码。当equals返回true,我们在逻辑上可以认为是同一个对象,但是查看哈希码却发现哈希码不同,和equals方法的返回结果相悖。而且object定义的hashcode方法生成的哈希值跟对象的本身属性值是无关的。
重写hashcode之后,我们可以自定义哈希码的生成规则。可以通过对象的属性值计算哈希码。在后面的HashMap中,借助equals和hashcode方法来存储数据。
而HashMap底层是如何存储数据的在《1.12Java-集合》里的1.12.3Map集合 小节有详细讲解。
补充知识点:
(值类型的变量直接存储数据,而引用类型的变量是数据的引用)
值类型是存储在内存中的栈,引用类型,变量在栈中,本身在堆中,栈中仅仅存储引用类型变量的地址。
严格点 ,在内存模型图中,==是比较栈中的值,equals比较的是否是同一个地址
1.9.3.4.2 String为什么被final修饰
String变量对象赋值后,会在常量池生成一个常量,栈中有该常量的引用,如果字符串改变,则会copy一个新的值
Java将String用final修饰原因有以下几点:
1、实现字符串池(可以提高程序的效率) 。String是不可被继承的,value[]被存储的数据不可更改性(引用地址不可更改,数组本身可变),但是value[]是private修饰的,而且String是不可被继承的,所以保证了字符串不可变性 。
2、线程安全。当调用其他方法时,例如调用一些系统级操作指令之前,可能会有一系列校验,如果是可变类的话,有可能在校验后,String的内部的值又被改变了,这样可能会引起严重的系统崩溃问题。因为字符串是不可变的,所以是多线程安全的。
3、实现String可以创建HashCode不可变性 。由于字符串是不可变的 ,所以在它创建的时候HashCode就被缓存了,不需要重新计算。这就使得字符串很适合作为Map中的键,字符串的处理速度要快过其它的键对象。这就是HashMap中的键往往都使用字符串。
1.9.4StringBuilder
1.9.4.1什么是StringBuilder
StringBuilder是一个可变的字符串类,我们可以把它看成是一个容器,这里的可变指的是StringBuilder对象中的内容是可变的带有缓冲区的字符串类,可以理解为将数据直接存储在对象,而不是存储地址,那么修改时就是修改本身。
1.9.4.2StringBuilder构造方法
方法名 | 说明 |
---|---|
public StringBuilder() | 创建一个空白可变字符串对象,不含有任何内容 |
public StringBuilder(String str) | 根据字符串的内容,来创建可变字符串对象 |
1.9.4.3StringBuilder类常见方法
方法名 | 说明 |
---|---|
public StringBuilderappend(Object obj) | 添加数据,并返回对象本身 |
public StringBuilderreverse() | 返回相反的字符序列 |
1.9.4.4StringBulider与String的转化
-
StringBuilder转换为String
public String toString():通过 toString() 就可以实现把 StringBuilder 转换为 String
-
String转换为StringBuilder
public StringBuilder(String s):通过构造方法就可以实现把 String 转换为 StringBuilder
示例
//StringBuilder转化String
StringBuilder sb = new StringBuilder("test");
String s =sb.toString();
//String转化StringBuilder
String s = "test";
StringBuilder sb = new StringBuilder(s);
1.9.5StringBuffer
1.9.5.1什么是StringBuffer
字符串变量(Synchronized,即线程安全)。如果要频繁对字符串内容进行修改,出于效率考虑最好使用 StringBuffer,如果想转成 String 类型,可以调用 StringBuffer 的 toString() 方法。
StringBuffer 上的主要操作是 append 和 insert 方法:
append 方法始终将这些字符添加到缓冲区的末端;
insert 方法则在指定的点添加字符
1.9.5.2StringBuffer构造方法
方法名 | 说明 |
public StringBuffer() | 构造一个其中不带字符的字符串缓冲区,其初始容量为 16 个字符。 |
public StringBuffer(int capacity) | 指定容量的字符串缓冲区对象 |
public StringBuffer(String str) | 指定字符串内容的字符串缓冲区对象 |
1.9.5.3StringBuffer的方法
append(String str) | 可以把任意类型添加到字符串缓冲区里面,并返回字符串缓冲区本身 |
Buffer insert (int offset,String str) | 在指定位置把任意类型的数据插入到字符串缓冲区里面,并返回字符串缓冲区本身 |
其他方法可查找API了解学习
1.9.6String、StringBuilder、StringBuffer的区别
-
String类
:内容是不可变的 -
StringBuilder类
:内容是可变的 -
StringBuilder:线程不安全,运行效率高
-
StringBuffer :内容是可变的 线程安全
1.9.6.1如何理解线程安全与线程不安全
在这里,我们暂且只简单了解StringBuilder与StringBuffer所涉及的线程安全方面理论,关于更加详细的线程会在后面的章节详细讲解。
什么是线程?
线程是操作系统调度的最小单位(进程是资源分配的最小单位)。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
比如,迅雷是一个进程,迅雷当前下载任务就是线程
java虚拟机 是个进程,里面就包含默认主线程(main),可通过代码创建多个独立线程,与main并发执行
那么为什么StringBuilder与StringBuffer会涉及到线程安全方面?
假设 a b 两个线程读取同一个file文件
a.read(file);
b.write(file);
由于是多线程,每个线程的运行时间由cpu分配,所以会出现:
开始
a的时间片 a读取一点结束
b的时间片 b写入一点结束
a的时间片 a读取一点结束
b的时间片 b写入一点结束
结束
这时候出现问题
有可能a想要读取的数据还没有结束,时间到了,cpu会分配时间给b,b正好修改的是a要读取的数据
这就乱套了
这就是线程安全问题。
StringBuffer的源码里面都有synchronized修饰,这个关键字就是线程同步关键字。
有了synchronized修饰,当一个线程正在访问数据时,另一个线程是无法对正被访问的数据进行操作的,进而保证了数据的安全。
而StringBuilder没有synchronized修饰。
1.9.7 日期和时间
1.9.7.1 Calendar
1.9.7.1.1 获取年月日小时分钟秒
java.util.Calendar,调用其 get()方法传入不同的参数即可获得参数所对应的值。Java 8 中可以使用 java.time.LocalDateTimel 来获取
public class DateTimeTest {
public static void main(String[] args) {
Calendar cal = Calendar.getInstance();
System.out.println(cal.get(Calendar.YEAR));
System.out.println(cal.get(Calendar.MONTH)); // 0 - 11
System.out.println(cal.get(Calendar.DATE));
System.out.println(cal.get(Calendar.HOUR_OF_DAY));
System.out.println(cal.get(Calendar.MINUTE));
System.out.println(cal.get(Calendar.SECOND));
// Java 8
LocalDateTime dt = LocalDateTime.now();
System.out.println(dt.getYear());
System.out.println(dt.getMonthValue()); // 1 - 12
System.out.println(dt.getDayOfMonth());
System.out.println(dt.getHour());
System.out.println(dt.getMinute());
System.out.println(dt.getSecond());
}
}
1.9.7.1.2 获取从1970年到现在的毫秒数
1970 年 1 月 1 日 0 时 0 分 0 秒到现在的毫秒数
Calendar.getInstance().getTimeInMillis();
System.currentTimeMillis();
Clock.systemDefaultZone().millis(); // Java 8
1.9.7.1.3 获取某月的最后一天
Calendar time = Calendar.getInstance();
time.getActualMaximum(Calendar.DAY_OF_MONTH);
1.9.7.2 格式化日期
利用 java.text.DataFormat 的子类(如 SimpleDateFormat 类)中的 format(Date)方法可将日期格式化。Java 8 中可以用 java.time.format.DateTimeFormatter 来格式化时间日期,
class DateFormatTest {
public static void main(String[] args) {
SimpleDateFormat oldFormatter = new SimpleDateFormat("yyyy/MM/dd");
Date date1 = new Date();
System.out.println(oldFormatter.format(date1));
// Java 8
DateTimeFormatter newFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
LocalDate date2 = LocalDate.now();
System.out.println(date2.format(newFormatter));
}
}