JavaSE
一.Object类
- Java中所有类的直接或者间接父类。
- 只有无参构造方法。所以我们定义的类中,才都有无参构造方法。
1.getClass():
本质:在类加载的时候,JVM会为这个类生成一个Class对象,方便我们使用者获得这个类的信息。这个Class对象可以类比于这个类,类和Class对象是一一对应的关系。通过面向对象的语法,访问Class对象我们可以获得类定义的相关信息。
Class对象所对应的Class类是抽取了什么共性而组成的呢?
因为Class类都能:(共性)
- 可以定义构造方法。
- 可以定义成员变量。
- 可以定义成员方法。
Class对象在1.7及以前存放在方法区,1.7之后存在堆中。
作用:
返回此对象运行时的类。
package day11;
public class Demo1GetClass {
public static void main(String[] args) {
Test test = new Test();
Class obj = test.getClass();
System.out.println(obj.getName());
}
}
class Test{
int i;
public void print(){
}
}
2.toString()
作用:
- 返回该对象的字符串表示。
- toString方法会返回一个”以文本方式表示“对象字符串,在没有重写时,默认输出 getClass().getName() + ‘@’ + Integer.toHexString(hashCode())。因此我们一般要重写这个方法,输出它的属性。
注:
//test为对象名
System.out.println(test); //自动调用toString()
String str = "hello, " + test; //同上
System.out.println(str);
3.equals()
作用:
指示其他对象与该对象是否相等。(这里的相等是指两个对象是完全相同的两个对象)我们一般要重写这个equals()方法。让两个对象只要变量相同即可。在改写时要遵守几个定义:
- 自反性 (条件一)
- 对称性
- 传递性
- 一致性
- null与任何其他对象都不相等。 (条件二)
public class Demo1Equals {
}
class Test{
int i;
double j;
@Override
public boolean equals(Object obj) {
if( obj == this )
return true;
if(obj == null || this.getClass() != obj.getClass())
return false;
Test obj1 = (Test)obj; //obj是Object类型,没有i,j等成员变量,这里需要强转类型。
if(this.i != obj1.i)
{
return false;
}
return Double.compare(this.j, obj1.j) == 0; //double之间的精确比较应该通过这个方法。返回0则相同,返回非0则不同。
}
}
4.hashCode()
作用:
返回该对象的哈希码值。
hash函数作用:
A hash function is any function that can be used to map data of arbitrary size onto data of fixed size.
能把无限多的值映射到固定多的值中
对象的hash码值
Object类定义的默认实现一般是通过对象的内部地址转化成一个整数。
与equals()的关系
未重写的equals方法的结果为true时,在没有改equals方法的前提下,则hash码应该相同。但是两对象equals不等,hash码却不一定不同。因为很难做都一对一对应。
5.clone()
作用:
protected Object clone()
创建并返回此对象的一个副本
这个副本是浅拷贝。是完全生成一个全新的对象。但是浅拷贝不会去生成一个新的引用成员变量所指向的对象,只会单纯拷贝引用变量的地址值,致使拷贝出来的对象指向同一个引用对象。
clone()的权限问题。
在同包的两个类中,当我们在一个类中调用另一个类的对象的clone()方法时,即使clone方法时protected权限的,还是会出现权限问题,因为clone方法都是继承自Object的跨包方法,protected权限的方法跨包时除了在子类中,其他情况是不能访问的,此时的解法只有在调用了clone()方法的类定义下,重载clone()方法,让这个方法与两个类同包,才能使用。
标记接口:
被clone的类必须实现Cloneable接口,该接口是一个空接口。这是一个编程技巧。实现了接口之后,该类可在 instanceof Cloneable时返回true。类可以被看做是Cloneable接口类型了。
将clone()方法改写成一个深拷贝
class FirstLevel implements Cloneable {
int firstInt;
double firstDouble;
SecondLevel second;
@Override
public String toString() {
return "firstInt = " + firstInt + " firstDouble = " + firstDouble + " second = {" + second + "}";
}
public FirstLevel(int firstInt, double firstDouble, SecondLevel second)
{
this.firstInt = firstInt;
this.firstDouble = firstDouble;
this.second = second;
}
@Override
protected Object clone() throws CloneNotSupportedException {
FirstLevel clone = (FirstLevel)super.clone(); //利用Object的clone()方法,先对当前的FirstLevel进行浅拷贝。
clone.second = (SecondLevel)clone.second.clone(); //针对复制对象中的引用类型变量进行拷贝
return clone;
}
}
6.finalize()
当垃圾回收器确定不存在该对象的更多引用时,对象的垃圾回收器会自己调用这个方法,但不是马上调用。
用户可以重写这个方法,达到配置系统资源,执行其他清除的作用。
二.String类
1.定义:
字符串是由多个字符组成的一串数据。
2.构造方法:
- public String() -----> 创建一个空串:"" 一般不用
- public String(byte[] bytes)
byte[] bytes = {97, 98, 99};
String s = new String(bytes);
System.out.println(s);
//输出 abc
- public String(byte[] bytes, int offset, int length)
byte[] bytes = {97, 98, 99};
String s = new String(bytes, 1, 2);
//输出 bc
- public String(char[] value)
char[] value = {'a', 'b', 'c'};
String s = new String(value);
- public String(char[] value, int offset, int length)
- public String(String orignal)
char[] value = {'a', 'b', 'c'};
String s = new String(value);
String s2 = new String(s);
System.out.println(s2)
System.out.println(s == s2); //输出false
s.equals(s2) //输出true
//可以看出String类中重写了equals方法。
3.String在Java中的一个特征
字符串是”常量“,但不是字面上的常量。它的值在创建后就不能再更改。
这里的“不可再更改”的意思是,当我们创建一个字符串对象后,如果改变这个字符串对象的内容,则会自动再生成一个字符串对象,让引用变量指向新的字符串对象。
4.面试问题
第一题:
String s = "hello";
s += "world";
s的结果为多少。 ----> "helloworld"
第一句:
“hello”为字符串字面值常量,存储在字符串常量池中,存储的方式仍然是以一个字符串对象来存储的。
首先判断是否存在这个字符串字面值常量,如果存在,则将该对象的地址直接赋给引用变量s。如果不存在则在常量池中创建一个字符串对象,再将地址赋值给引用变量s。
第二句:
首先判断“world”字符串常量有没有在常量池中,有则下一步,没有则在字符串常量池中创建一个字符串常量"world"。由于字符串是“常量”,是不可改变的,所以需要在堆中创建一个新的字符串对象,将“hello”和“world”拼接起来存入对象。将首地址赋值给引用变量s。
注:字符串常量池在jdk1.6及以前存储在方法区中,1.7开始存储在堆中,一般还是以存在方法区中为准。
第二题:
String s1 = new String("hello");
String s2 = "hello"; 之间的区别
第一句:
首先在字符串常量池中寻找是否存在字符串常量“hello”,若没有则创建一个字符串对象存进字符串常量“hello”,这个字符串对象的字符序列是存储在一个char字符数组中的。
再在堆中创建一个String类的对象,将存储字符串常量“hello”的对象中的char字符数组的首地址赋值给堆中的String类对象的char字符数组,两者指向同一个“hello”字符数组。
再在栈帧中创建String类型的引用变量s1存入堆中String类型对象的首地址,使其指向堆中的String对象。
第二句:
在字符串常量池中查找是否有匹配的字符串。找到后,将其首地址赋值给s2。
第三题:
String s1 = "hello";
String s2 = "world";
String s3 = "helloworld";
System.out.println(s3 == (s1 + s2)); //false。因为s1+s2会指向堆中的一个新对象。而s3是指向常量池中的一个字符串对象。
System.out.println(s3.equals("hello" + "world")); //true。String改写了equals方法,所以字面值相同即可。
System.out.println(s3 == ("hello" + "world")); // true。因为常量是已知的,JVM会将常量直接拼接,在.class上可以体现----> s3 == "helloworld";(或者final修饰的引用变量,也直接看成常量。)
总结:
- 参与拼接的字符串中有一个引用变量,则要在堆上创建新的字符串对象。
- 只有参与拼接的字符串都是字符串常量,此时不会在堆上创建新的字符串对象,而是直接拼接,如果常量池没有则在常量池创建。
- 引用变量如果被final修饰,则可看成是字符串常量。
5.功能
①判断功能
- boolean equals(Object obj) //用来比较字符串的内容
- boolean equalsIgnoreCase(String str) //忽略字符串大小写比较字符串内容
- boolean contains(String str) //判断当前字符串对象是否包含,目标字符串的字符序列
- boolean startsWith(String str) //判断当前字符串对象,是否已目标字符串的字符序列开头
- boolean endsWith(String str)// 判断当前字符串,是否以目标字符串对象的字符序列结尾
- boolean isEmpty() 判断一个字符串,是不是空字符串
注: - 空字符串:""
- null代表一个无效的内存地址,是不存在对象的。
②获取功能
- int length() // 获取当前字符串对象中,包含的字符个数
- char charAt(int index) // 获取字符串对象代表字符序列中,指定位置的字符(字符的位置从0开始编号)
- int indexOf(char ch) // 在当前字符串对象中,查找指定的字符,如果找到
// 就返回字符,首次出现的位置,如果没找到返回-1 - int indexOf(String str) // 查找当前字符串中,目标字符串首次出现的位置(如果包含)
// 找不到,返回-1, 这里的位置是指目标字符串的第一个字符
// 在当前字符串对象中的位置 - int indexOf(int ch,int fromIndex) // 指定,从当前字符串对象的指定位置开始,查找首次出现的指定字符的位置(如果没找到返回-1)
- int indexOf(String str,int fromIndex) // 指定,从当前字符串对象的指定位置开始,查找 首次 出现的指定字符串的位置(如果没找到返回-1)
- String substring(int start) // 返回字符串,该字符串只包含当前字符串中,从指定位置开始(包含指定位置字符),的那部分字符串
“zhangsan” -> “san” - String substring(int start,int end) // 返回字符串,只包含当前字符串中,从start位置开始(包含),到end(不包含)指定的位置,
// 这部分字符串 [start, end) ->[start, end - 1]
注: - API中形参包含start和end范围的,取值范围都是[start,end)。
③转换功能
- byte[] getBytes() // 获取一个用来表示字符串对象字符序列的字节数组
- char[] toCharArray() // 获取的是用来表示字符串对象字符序列的字符数组
- static String valueOf(char[] chs) // 把字符数组转化为字符串
- static String valueOf(int i) //把整数转化成其字符串表示形式
- String toLowerCase() //把字符串全部转化为小写
- String toUpperCase() // 把字符串全部装华为大写
- String concat(String str) //字符串拼接 作用等价于 + 实现的字符串拼接
④替换功能
- String replace(char old,char new) // 在新的字符串中,用新(new)字符,替换旧(old)字符
- String replace(String old, String new) //在新的字符串中,用新的字符串(new), 替换旧(old)字符串
⑤去空格功能
String类去除空格
- String trim() //在新的字符串中,去掉开头和结尾的空格字符
⑥比较功能:
- int compareTo(String str)
- int compareToIgnoreCase(String str) //忽略大小写的比较
compare的源代码
public interface Comparable<T> {
int compareTo(T o)
}
/*
该接口的意义是定义了一种比较大小的协议。
1.interface Comparable<T> 这里的T代表某种数据类型。
2.该接口定义了一种比较规则:比较结果大于,小于,等于关系与int整数映射关系
对象1.compareTo(对象2)
1. 大于: 对象1 > 对象2 -> > 0 正整数表示大于关系
2. 小于: 对象1 < 对象2 -> < 0 负整数表示小于关系
3. 等于: 对象 等于 对象2 -> = 0 0表示相等关系
*/
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
private final char value[]; //这个就是指String对象中存放字符的那个字符数组。
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2); //取小的那个
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2; //巧妙
}
k++;
}
return len1 - len2; //巧妙
}
}
作业附加题:
/*
附加题:
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
注意:如果有多个最长回文串,返回其中一个即可
*/
package day13;
import java.util.Scanner;
public class Exercise03 {
public static void main(String[] args) {
//解法一: 以每个位为对称轴或者以每个位和下一位为对称轴
Scanner in = new Scanner(System.in);
System.out.println("请输入一个String:");
String str = in.nextLine();
in.close();
if(str.length() > 1000) {
System.out.println("不好意思,超过了1000个字符");
return;
}
if(str.length() == 0) {
System.out.println("String为空");
return;
}
String subStr = "";
for(int i = 0; i < str.length() - 1; ++i)
{
String sub1 = findLongestString(str,i,i);
String sub2 = findLongestString(str,i,i+1);
String sub = sub1.length() > sub2.length() ? sub1 : sub2;
if(sub.length() > subStr.length())
subStr = sub;
}
System.out.println(subStr);
}
private static String findLongestString(String str, int start, int end)
{
char[] target = str.toCharArray();
while(start >= 0 && end < str.length() && target[start] == target[end]) {
start--;
end++;
}
start++;
end--;
return new String(target,start,end - start + 1); //巧妙运用构造函数。
}
}
/*
解法二:暴力
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.println("请输入一个String:");
String str = in.nextLine();
in.close();
if(str.length() > 1000) {
System.out.println("不好意思,超过了1000个字符");
return;
}
if(str.length() == 0) {
System.out.println("String为空");
return;
}
String subStr = "";
int length = 0;
for(int i = 0; i < str.length() - 1; ++i)
{
for(int j = i + 1; j < str.length(); ++j)
{
if(isPalindrome(str,i,j))
{
if((j - i + 1) > length)
{
subStr = str.substring(i,j+1);
length = j - i + 1;
}
}
}
}
System.out.println("最长的回文串为:" + subStr);
}
public static boolean isPalindrome(String str, int start, int end)
{
while(start < end)
{
if(str.charAt(start) != str.charAt(end))
return false;
else
{
start++;
end--;
}
}
return true;
}
*/
6.StringBuffer
String VS StringBuffer:
String类型是不可变的,每次拼接都要重新创建一个对象,再将拼接的结果存入耗费CPU资源。
StringBuffer是一个线程安全的可变字符序列。一个类似String的字符串缓冲区,但是可以修改。
构造方法
1.
public StringBuffer()
StringBuffer s = new StringBuffer(); //capacity默认为16
2.
public StringBuffer(int capacity) //可以通过自己的预估确定capacity大小,因为扩容所需要消耗的时间还是蛮多的。
3.
public StringBuffer(String str) //StringBuffer的初始capacity为 16 + str.length()
StringBuffer stringBuffer = new StringBuffer()
int length = stringBuffer.length() //字符缓冲区中真正包含的字符个数
int capacity = stringBuffer.capacity() // 字符缓冲区本身的大小
添加功能
public StringBuffer append(任意类型 str) // 把其转变为字符串类型,向字符串缓冲区尾部添加字符串
public StringBuffer insert(int offset, 任意类型 str) //在offset位置添加str,而不是在offset前插入。
int i = 10;
stringBuffer.append(i);
System.out.println(stringBuffer.toString()); //10 都是以字符串输出
String s1 = "jiang";
stringBuffer.insert(1,s1);
System.out.println(stringBuffer.toString()); //1jiang0
//链式调用
stringBuffer.insert(1,"haha").append(123).append(false); //insert,append返回的都是StringBuffer类型对象,且该对象是其本身 return this;
Other api
删除:
public StringBuffer deleteCharAt(int index)
public StringBuffer delete(int start, int end) // [start,end)
StringBuffer stringBuffer1 = stringBuffer.deleteCharAt(1); // 删除第二个字符。
.delete(0,10) //删除第1到第10个字符,下标从0 - 9
替换:
public StringBuffer replace(int start, int end, String str) [start,end)
StringBuffer s = stringBuffer.replace(0,2,"ahahahah");
反转:
public StringBuffer reverse() //反转字符缓冲区中的字符序列
StringBuffer s = stringBuffer.reverse();
截取:
public String substring(int start)
public String substring(int start, int end) //返回一个String,StringBuffer本身不变。 [start,end)
String s = stringBuffer.substring(0,3);
StringBuffer VS StringBuilder
JDK 5开始有了一个单线程使用的等价类 StringBuilder,它在单线程环境下与StringBuffer没有区别。StringBuffer会保障多线程的运行。(待补充)
Date类
构造方法/getTime()
Date date = new Date()
System.out.println(date) // Fri Jan 10 16:32:09 CST 2020
Date date = new Date(long date) //long date参数是从1970年1月1日 00:00:00 GMT 以来到现在,此 Date 对象表示的毫秒数。
//getTime()方法
long time = date.getTime(); // 从1970 年 1 月 1 日 00:00:00 GMT到date所表示时间的经历的毫秒数
//从time中提取对应的year
long year = time / (1000L * 60 * 60 * 24 * 365) // 求从1970到date是多少年
//用第二种构造方式的好处:
Date date1 = new Date(date.getTime() + (1000L * 60 * 60 * 24 * 2)) //求date后两天的date对象。
DateFormat类
是一个日期/时间 格式化 子类的 抽象类。
一般使用 SimpleDateFormat这个实现子类。
SimpleDateFormat
y 年
M 月
d 日
H 时
m 分
s 秒
public Date parse(String source) //parse:解析
public final String format(Date date) // format:格式,版本
SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); //设定你所要的格式
Date date = format.parse("1999/12/21 01:11:20"); //指定格式化为Date的传统格式
System.out.println(date) //输出传统形式。
Date date = new Date();
String format1 = format.format(date); //化为指定格式
System.out.println(format1);
Math类
- public static int abs(int a) //求绝对值
- public static double ceil(double a) // 取整 向大的方向取整
- public static double floor(double a)// 取整 向小的方向取
- public static int max(int a,int b) min自学
- public static double pow(double a,double b) a^b
- public static double random() // 返回(带正号)的 double 值,该值大于等于 0.0 且小于 1.0 [0.0 1.0) 随机数。
- public static int round(float a) 取整 4舍五入的取整
- public static double sqrt(double a) 返回正确舍入的 double 值的正平方根