[TOC]
Object类
深浅拷贝
应用场景
- 当一个对象A和对象B同出,只是些许特性有所变化,此时如果能直接复制一份A,然后修改成B的特有属性就高效很多了
- 属性
- 不仅包括基本数据类型,还有引用类型
- 日后如果需要修改B的属性,A的属性不应有所变化,A和B是不同的对象,各自在内层中占有空间
解决措施
- 实现一个Cloneable的接口,Cloneable接口不具有任何方法,仅仅作为标志为Object类中的clone()方法服务,如果clone类没有实现Cloneable接口,并调用了clone()方法,那么Object类的clone()方法会抛出CloneNotSupportedException异常
浅拷贝
- 将对象B的基本数据类的修改并不会引起A的变化,但是对引用类型的修改会改动A的属性
例子
package model.two_week;
public class ObjectCloneDemo2 {
public static void main(String[] args) {
Student s1 = new Student();
s1.setName("s1");
s1.setAge(20);
Teacher t1 = new Teacher();
t1.setName("张三");
t1.setAge(30);
t1.setSubject("语文");
s1.setTeacher(t1);
System.out.println(s1);
try {
Student s2 = (Student) s1.clone();
s2.setName("s2");
s2.setAge(22);
Teacher t2 = s2.getTeacher();
t2.setAge(45);
t2.setName("李四");
t2.setSubject("数学");
System.out.println(s2);
System.out.println(s1);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
class Person {
protected String name;
protected int age;
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;
}
}
class Student extends Person implements Cloneable{
protected Teacher teacher;
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", teacher" + teacher +
'}';
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Teacher extends Person {
protected String subject;
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
@Override
public String toString() {
return "{" +
"name='" + name + '\'' +
", age=" + age+
", subject=" + subject +
'}';
}
}
输出结果
Student{name='s1', age=20, teacher{name='张三', age=30, subject=语文}}
Student{name='s2', age=22, teacher{name='李四', age=45, subject=数学}}
Student{name='s1', age=20, teacher{name='李四', age=45, subject=数学}}
可以看出,指向统一引用类型的s1和s2,当s2修改Teacher属性后,改变了s1
深拷贝
- 无论修改B的哪一属性都不会对A造成任何影响
例子
package model.two_week;
public class ObjectCloneDemo {
public static void main(String[] args) {
Student s1 = new Student();
s1.setName("s1");
s1.setAge(20);
Teacher t1 = new Teacher();
t1.setName("张三");
t1.setAge(30);
t1.setSubject("语文");
s1.setTeacher(t1);
System.out.println(s1);
try {
Student s2 = (Student) s1.clone();
s2.setName("s2");
s2.setAge(22);
Teacher t2 = (Teacher) t1.clone();//拷贝Teacher属性的第一种方式,后需要给s2设置Teacher
// Teacher t2 = s2.getTeacher();//拷贝Teacher属性的第二种方式,直接从s2学生的Teacher属性获取
t2.setAge(45);
t2.setName("李四");
t2.setSubject("数学");
s2.setTeacher(t2);
System.out.println(s2);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
class Person implements Cloneable {
protected String name;
protected int age;
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;
}
@Override
public String toString() {
return "{name:" + this.name + " age:" + this.age + "}";
}
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Student extends Person {
protected Teacher teacher;
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", Teacher" + teacher +
'}';
}
}
class Teacher extends Person {
protected String subject;
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
}
输出结果
Student{name='s1', age=20, Teacher{name=张三', age=30, subject=语文}}
Student{name='s2', age=22, Teacher{name=李四', age=45, subject=数学}}
Student{name='s1', age=20, Teacher{name=张三', age=30, subject=语文}}
可见,实现了Cloneable接口的Teacher属性不会因为s2的改动而改动s1
equals()和hashcode()
-
equals()和==都是比较两者是否相等的方法。在Object基类中,equals()方法的代码如下:
public boolean equals(Object obj) { return (this == obj); }
可以看出,equals()和==使用效果等价,但有所区别:
- equals()是方法,而==是操作符
- == 对于基本数据类型,判断其两边变量的值是否相等;对于引用类型,判断其地址值是否相等
-
但是在下一个例子中,equals()和==方法的结果却并不一致
例子
public class equals_hashcode {
public static void main(String[] args) {
String s1 = "abcd";
String s2 = "abcd";
String s3 = new String("abcd");
String s4 = new String("abcd");
System.out.println("s1==s2 "+(s1==s2));//true,比较的是基本数据类型的内容
System.out.println("s3==s4 "+(s3==s4));//false,比较的是两个引用类型的地址值
System.out.println("s1==s3 "+(s1==s3));//false,比较的是两个不同数据类型
System.out.println("s1.equals(s2) "+s1.equals(s2));//true,比较得是字符串的值
System.out.println("s3.equals(s4) " + s3.equals(s4));//true,比较的是字符串的值
System.out.println("abcd==abcd "+("abcd"=="abcd"));//true,比较的是两个基本数据类型的字符串的值
}
}
其原因在于,继承Object的子类String复写了equals()方法
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
上述equals()的实现方式在于hashCode()
- Object基类中的方法为
public native int hashCode();
其native为本地方法,其默认返回对象的内存地址
- 其String类中复写的代码为
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
hash算法(哈希算法)
-
概念:将对象本身的键值对,通过特殊的数学函数或其他方法,转化成相应的数据存储地址
-
目的:通过地址值用来快速获取其元素
-
哈希碰撞:存在不同的数据经过哈希算法得到相同的地址值
-
hashCode()方法充分运用了hash算法,能实现快速获取元素并进行比较的功能
结论
- 调用equals()返回true的两个对象必须具有相同的哈希码
- 如果结果为false,证明其相同值的hash运算结果不一致,违背hash算法
- 如果两个对象的hashCode值相同,调用equals()不一定返回true
- 因为存在哈希碰撞现象
getClass和Class
-
调用getClass和Class方法,起返回值都是其父类
-
区别:
- getClass是实例方法,需要new 对象后才可以使用,且其是在jvm运行后才得出
- Class是类类型方法,通过JVM编译程序后生成的.class文件,获取结果
-
原理:
- 反射:可以理解是一种反编译的过程,JVM通过java文件获取其内部属性和方法的过程。但这不是Java的独一特性,任何一种语言都可以实现反射机制。
-
例子
public class getClass { public static void main(String[] args) { Animal animal = new Animal(); Cat cat = new Cat(); Animal tiger = new Cat(); Animal animal1 = new Animal(); System.out.println(animal.getClass() + "---" + Animal.class); System.out.println(cat.getClass() + "---" + Cat.class); System.out.println(tiger.getClass() + "---" + Cat.class); System.out.println(animal1.getClass() + "---" + Animal.class); tiger = animal1; System.out.println(tiger.getClass() + "---" + Animal.class); } } class Animal { public void function() { } } class Cat extends Animal { }
输出结果:
//class Animal---class Animal //class Cat---class Cat //class Cat---class Cat //class Animal---class Animal //class Animal---class Animal
注意第三个和第五个,tiger重新赋值后,tiger.getClass()在运行结束之后再显示最终结果
toString
-
直接看Object基类里的toString方法
public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); }
-
getClass是实例化后的方法,返回值是其父类,getName()是Object基类里有的方法,返回其name属性,“@”分隔后。Integer.toHexString(hashCode())返回其对象在内存中的地址值,注意的是,其内存值以16进制的哈希值表示
-
文件关于方法的说明如下:
/** *Returns a string representation of the object. In general, the * {@code toString} method returns a string that * "textually represents" this object. The result should * be a concise but informative representation that is easy for a * person to read. * It is recommended that all subclasses override this method. */
要求子类必须复写toString方法
-
而String类中的toString如下
public String toString() { return this; }
-
String类
compareTo与compare
compareTo
-
是接口Comparable唯一需要实现的方法
-
直接看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; }
-
根据其说明文档
/** * Compares two strings lexicographically. * The comparison is based on the Unicode value of each character in * the strings. The character sequence represented by this * {@code String} object is compared lexicographically to the * character sequence represented by the argument string. The result is * a negative integer if this {@code String} object * lexicographically precedes the argument string. The result is a * positive integer if this {@code String} object lexicographically * follows the argument string. The result is zero if the strings * are equal; {@code compareTo} returns {@code 0} exactly when * the {@link #equals(Object)} method would return {@code true}. * <p> * This is the definition of lexicographic ordering. If two strings are * different, then either they have different characters at some index * that is a valid index for both strings, or their lengths are different, * or both. If they have different characters at one or more index * positions, let <i>k</i> be the smallest such index; then the string * whose character at position <i>k</i> has the smaller value, as * determined by using the < operator, lexicographically precedes the * other string. In this case, {@code compareTo} returns the * difference of the two character values at position {@code k} in * the two string -- that is, the value: * <blockquote><pre> * this.charAt(k)-anotherString.charAt(k) * </pre></blockquote> * If there is no index position at which they differ, then the shorter * string lexicographically precedes the longer string. In this case, * {@code compareTo} returns the difference of the lengths of the * strings -- that is, the value: * <blockquote><pre> * this.length()-anotherString.length() * </pre></blockquote> * * @param anotherString the {@code String} to be compared. * @return the value {@code 0} if the argument string is equal to * this string; a value less than {@code 0} if this string * is lexicographically less than the string argument; and a * value greater than {@code 0} if this string is * lexicographically greater than the string argument. */
可知
- 按照字典顺序进行比较
- 比较的是两者的Unicode
- 如果前者比后者顺序在字典中顺序靠前,即Unicode较小,返回负整数,反之返回正整数,只有当调用equals方法返回值为true时才返回0
- 比较方法,先判断出两者长度较小的一个,根据相同索引的值比较大小,以最早得到的差值作为结果;否则,如果长度相等则将长度相减作为结果返回
compare
-
是Comparator接口的方法
-
其String中的代码如下
public int compare(String s1, String s2) { int n1 = s1.length(); int n2 = s2.length(); int min = Math.min(n1, n2); for (int i = 0; i < min; i++) { char c1 = s1.charAt(i); char c2 = s2.charAt(i); if (c1 != c2) { c1 = Character.toUpperCase(c1); c2 = Character.toUpperCase(c2); if (c1 != c2) { c1 = Character.toLowerCase(c1); c2 = Character.toLowerCase(c2); if (c1 != c2) { // No overflow because of numeric promotion return c1 - c2; } } } } return n1 - n2; }
-
其方法实现:
- 类似于compareTo
异同
- compare常用于排序和分组,这个设计到之后的序列化内容,暂时留存,之后补充
concat
说明文档
* Concatenates the specified string to the end of this string.
* <p>
* If the length of the argument string is {@code 0}, then this
* {@code String} object is returned. Otherwise, a
* {@code String} object is returned that represents a character
* sequence that is the concatenation of the character sequence
* represented by this {@code String} object and the character
* sequence represented by the argument string.<p>
* Examples:
* <blockquote><pre>
* "cares".concat("s") returns "caress"
* "to".concat("get").concat("her") returns "together"
* </pre></blockquote>
*
* @param str the {@code String} that is concatenated to the end
* of this {@code String}.
* @return a string that represents the concatenation of this object's
* characters followed by the string argument's characters.
*/
- 连接指定字符串到此字符串结尾,并返回组合的字符串
- 如果参数长度为0,则返回元字符串
源码
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
例子
public class Concat {
public static void main(String[] args) {
String i = "我";
String is = "是";
String a = "一位";
String beauty = "美女";
System.out.println(i.concat(is).concat(a).concat(beauty));//我是一位美女
System.out.println(i);//我
System.out.println(a.concat(beauty));//同样可以利用concat将字符串放置在此字符串之前//一位美女
}
}
equals和equalslgnoreCase、contentEquals
源码比较
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
public boolean contentEquals(CharSequence cs) {
// Argument is a StringBuffer, StringBuilder
if (cs instanceof AbstractStringBuilder) {
if (cs instanceof StringBuffer) {
synchronized(cs) {
return nonSyncContentEquals((AbstractStringBuilder)cs);
}
} else {
return nonSyncContentEquals((AbstractStringBuilder)cs);
}
}
// Argument is a String
if (cs instanceof String) {
return equals(cs);
}
// Argument is a generic CharSequence
char v1[] = value;
int n = v1.length;
if (n != cs.length()) {
return false;
}
for (int i = 0; i < n; i++) {
if (v1[i] != cs.charAt(i)) {
return false;
}
}
return true;
}
public boolean equalsIgnoreCase(String anotherString) {
return (this == anotherString) ? true
: (anotherString != null)
&& (anotherString.value.length == value.length)
&& regionMatches(true, 0, anotherString, 0, value.length);
}
其中equalsIgnoreCase调用的regionMatches方法如下:
public boolean regionMatches(int toffset, String other, int ooffset,
int len) {
char ta[] = value;
int to = toffset;
char pa[] = other.value;
int po = ooffset;
// Note: toffset, ooffset, or len might be near -1>>>1.
if ((ooffset < 0) || (toffset < 0)
|| (toffset > (long)value.length - len)
|| (ooffset > (long)other.value.length - len)) {
return false;
}
while (len-- > 0) {
if (ta[to++] != pa[po++]) {
return false;
}
}
return true;
}
异同
- equals仅限于String类的比较,且区分大小写
- equalsIgnoreCase限于String类的比较,不区分大小写
- contentEquals不限于String类,还包括StringBuffer等,仅比较value值,区分大小写(因为调用了equals方法)
例子
public class Equals {
public static void main(String[] args) {
System.out.println("Abc".equals("abc"));//false
System.out.println("Abc".equalsIgnoreCase("abc"));//true
System.out.println("Abc".contentEquals("abc"));//false
System.out.println("Abc".contentEquals(new StringBuffer("Abc")));//true
}
}
join
源码
public static String join(CharSequence delimiter, CharSequence... elements) { Objects.requireNonNull(delimiter); Objects.requireNonNull(elements); // Number of elements not likely worth Arrays.stream overhead. StringJoiner joiner = new StringJoiner(delimiter); for (CharSequence cs: elements) { joiner.add(cs); } return joiner.toString(); }
public static String join(CharSequence delimiter, Iterable<? extends CharSequence> elements) { Objects.requireNonNull(delimiter); Objects.requireNonNull(elements); StringJoiner joiner = new StringJoiner(delimiter); for (CharSequence cs: elements) { joiner.add(cs); } return joiner.toString(); }
说明文档
-
源码1 用于将指定的各个字符串连起来,如
String.join("-", "Java", "is", "cool"); * // message returned is: "Java-is-cool"
-
源码2用于将字符串用特定字符分隔,同时删去最后的分隔符,如
* List<String> strings = new LinkedList<>(); * strings.add("Java");strings.add("is"); * strings.add("cool"); * String message = String.join(" ", strings); * //message returned is: "Java is cool" * * Set<String> strings = new LinkedHashSet<>(); * strings.add("Java"); strings.add("is"); * strings.add("very"); strings.add("cool"); * String message = String.join("-", strings); * //message returned is: "Java-is-very-cool"
例子
public class Join {
public static void main(String[] args) {
String[] s1 = {"I","am","a","beauty"};
System.out.println(String.join("-","I","am","a","beauty"));//I-am-a-beauty
System.out.println(String.join("-",s1));
}//I-am-a-beauty
}
trim
源码
public String trim() {
int len = value.length;
int st = 0;
char[] val = value; /* avoid getfield opcode */
while ((st < len) && (val[st] <= ' ')) {
st++;
}
while ((st < len) && (val[len - 1] <= ' ')) {
len--;
}
return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
}
- 用法:删除字符串前后端的空格,并将结果返回
例子
public class Trim {
public static void main(String[] args) {
System.out.println(" abc ".trim());
}
}
输出结果为
abc
isEmpty
源码
public boolean isEmpty() {
return value.length == 0;
}
- 用法:判断字符串是否为空,为空返回true,否则返回false
例子
public class IsEmpty {
public static void main(String[] args) {
System.out.println("".isEmpty());//true
System.out.println(" ".isEmpty());//false
}
}
split
源码
- 对子字符串有限制,正整数表明最大返回数,负整数表明返回尽可能多,0可以排除尾随空字符串的所有子字符串
public String[] split(String regex, int limit) {
/* fastpath if the regex is a
(1)one-char String and this character is not one of the
RegEx's meta characters ".$|()[{^?*+\\", or
(2)two-char String and the first char is the backslash and
the second is not the ascii digit or ascii letter.
*/
char ch = 0;
if (((regex.value.length == 1 &&
".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) ||
(regex.length() == 2 &&
regex.charAt(0) == '\\' &&
(((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 &&
((ch-'a')|('z'-ch)) < 0 &&
((ch-'A')|('Z'-ch)) < 0)) &&
(ch < Character.MIN_HIGH_SURROGATE ||
ch > Character.MAX_LOW_SURROGATE))
{
int off = 0;
int next = 0;
boolean limited = limit > 0;
ArrayList<String> list = new ArrayList<>();
while ((next = indexOf(ch, off)) != -1) {
if (!limited || list.size() < limit - 1) {
list.add(substring(off, next));
off = next + 1;
} else { // last one
//assert (list.size() == limit - 1);
list.add(substring(off, value.length));
off = value.length;
break;
}
}
// If no match was found, return this
if (off == 0)
return new String[]{this};
// Add remaining segment
if (!limited || list.size() < limit)
list.add(substring(off, value.length));
// Construct result
int resultSize = list.size();
if (limit == 0) {
while (resultSize > 0 && list.get(resultSize - 1).length() == 0) {
resultSize--;
}
}
String[] result = new String[resultSize];
return list.subList(0, resultSize).toArray(result);
}
return Pattern.compile(regex).split(this, limit);
}
- 对子字符串无限制
public String[] split(String regex) {
return split(regex, 0);
}
Array类
前提:
-
出现的原因:对相同数据类型的数据能快速获取和利用,所以将他们存放在一个数组中
-
Array类是存储相同数据类型(基本/引用)、长度固定的、通过下标来获取元素
- 只能存储相同数据类型:因为java是强类型语言,所以声明了Array的数据类型后就不可改变
- 长度固定:因为java是静态的,一旦被初始化就不可修改
-
关于存储:
- 存储基本数据类型的数据,存放在栈内存中,且默认值为0
- 存储引用数据类型的数据,存放在堆内存中,且默认值为null
-
理解存放内存:
-
声明一个数组的时候,是在栈内存中存储intArr,其默认值为0/null
int[] intArr;
-
赋值后,会在堆内存中开辟新空间存储new int[],且将其地址值赋值给intArr
int[] intArr = new int[];
-
-
初始化:
-
静态初始化:
int[] intArr1 = {1,2,5,1,3,2};
一开始声明其数组元素
-
动态初始化:
int[] intArr2 = new int[5];
只是固定数组长度,内容之后动态创建
-
例子
public static class ArrayDemo {
public static void main(String[] args){
int[] arr ={2,6,8,4,7,1};
System.out.println(arr[1]);
getArr(arr);//打印数组
print(getMax(arr));//获取数组最大值
selectArr(arr);//排序数组
getArr(arr);//打印数组
}
public static void getArr(int[] arr){
System.out.print("[");
for(int x =0 ;x<arr.length;x++){
if(x!=arr.length-1) {
System.out.print(arr[x]+",");
}else
System.out.print(arr[x]+"]");
}
}
public static int getMax(int[] arr){
int max=arr[0];
for(int x=0;x<arr.length;x++){
if(arr[x]>max){
max=arr[x];
}
}return max;
}
public static void print(int num){
System.out.println(num);
}
public static int getMin(int[] arr){
int min=0;
for(int x = 0;x<arr.length;x++){
if(arr[x]<arr)
}
}
public static void selectArr(int[] arr){
for(int x=0;x<arr.length-1;x++){
for(int y=x+1;y<arr.length;y++){
if(arr[x]>arr[y]){
int temp=arr[x];
arr[x]=arr[y];
arr[y]=temp;
}
}
}
}
}
常用静态方法
- binarySearch(Object[] a,Object key):
- 二分法搜索元素,如果存在返回索引,如果不存在返回(-(插入点+1)
- 在调用之前需对数组排序
- equals :判断两数组是否相等
- fill(int[] a,(fromIndex,toIndex)int val):指定int数组内的每个元素赋值
- sort(Object[] a):按照升序给数组排序
例子
public class ArrayTool2 {
public static void main(String[] args) {
int[] arr = {1,9,5,4,3};
printArr(arr);
Arrays.sort(arr);
printArr(arr);
System.out.println(Arrays.binarySearch(arr, 8));
System.out.println(Arrays.binarySearch(arr, 4));
Arrays.fill(arr, 1, 2, 5);
printArr(arr);
}
public static void printArr(int[] var0) {
System.out.print("[");
for(int var1 = 0; var1 < var0.length; ++var1) {
if (var1 != var0.length - 1) {
System.out.print(var0[var1] + ",");
} else {
System.out.print(var0[var1] + "]\n");
}
}
}
}
输出结果
[1,9,5,4,3]
[1,3,4,5,9]
-5
2
[1,5,4,5,9]
网友评论