文章目录
Object
Object 类属于 java.lang 包,此包下的所有类在使用时无需手动导入,系统会在程序编译期间自动导入。Object 类是所有类的基类,当一个类没有直接继承某个类时,默认继承Object类,也就是说任何类都直接或间接继承此类,Object 类中能访问的方法在所有类中都可以调用。
##equals()
public boolean equals(Object obj) {
return (this == obj);
}
谈到equals就得来谈谈equal和==的区别:
== 是用来比较两个变量所指向的内存地址是否相同,可以用来两个基本类型的数据或两个引用变量是否相等。
在Object类中: equals方法的实现是 return this==obj
, 也就是说equals与 == 是一样的,比较的都是两个对象所指向的内存地址是否相同。但是在String类中重写了父类Obejct中的equls方法,比较的是两个对象的内容是否相同。
在Java中游8种基本数据类型:
浮点型:float(4 byte), double(8 byte)
整型:byte(1 byte), short(2 byte), int(4 byte) , long(8 byte)
字符型: char(2 byte)
布尔型: boolean(JVM规范没有明确规定其所占的空间大小,仅规定其只能够取字面值"true"和"false")
对于这8种基本数据类型的变量,变量直接存储的是“值”,因此在用关系操作符==来进行比较时,比较的就是 “值” 本身。要注意浮点型和整型都是有符号类型的,而char是无符号类型的(char类型取值范围为0~2^16-1).
也就是说比如:
int n=3;
int m=3;
变量n和变量m都是直接存储的"3"这个数值,所以用==比较的时候结果是true。
而对于非基本数据类型的变量,在一些书籍中称作为 引用类型的变量。比如上面的str1就是引用类型的变量,引用类型的变量存储的并不是 “值”本身,而是于其关联的对象在内存中的地址。比如下面这行代码:
String str1;
这句话声明了一个引用类型的变量,此时它并没有和任何对象关联。
而 通过new String(“hello”)来产生一个对象(也称作为类String的一个实例),并将这个对象和str1进行绑定:
str1= new String(“hello”);
那么str1指向了一个对象(很多地方也把str1称作为对象的引用),此时变量str1中存储的是它指向的对象在内存中的存储地址,并不是“值”本身,也就是说并不是直接存储的字符串"hello"。这里面的引用和C/C++中的指针很类似。
因此在用==对str1和str2进行第一次比较时,得到的结果是false。因此它们分别指向的是不同的对象,也就是说它们实际存储的内存地址不同。
而在第二次比较时,都让str1和str2指向了str指向的对象,那么得到的结果毫无疑问是true。
equals方法是基类Object中的方法,因此对于所有的继承于Object的类都会有该方法。为了更直观地理解equals方法的作用,直接看Object类中equals方法的实现。
其他的一些类诸如Double,Date,Integer等,都对equals方法进行了重写用来比较指向的对象所存储的内容是否相等。
getClass()
public final native Class<?> getClass();
返回对象的运行时类。通过这个类对象我们可以获取该运行时类的相关属性和方法。也就是Java中的反射,各种通用的框架都是利用反射来实现的。
Java中还有一种这样的用法,通过 类名.class 获取这个类的类对象 。
那么getClass与.class有什么区别呢?
结论:class 是一个类的属性,能获取该类编译时的类对象,而 getClass() 是一个类的方法,它是获取该类运行时的类对象。
public final native Class<?> getClass();返回的是一个 Class<?>,但是如下是能通过编译的:
Class<? extends String> c = "".getClass();
也就是说类型为T的变量getClass方法的返回值类型其实是Class<? extends T>而非getClass方法声明中的Class<?>。
hashCode()
public native int hashCode();
返回对象的哈希码值。
在Java应用程序的执行期间,无论何时在同一个对象上多次调用它,{@code hashCode}方法必须一致地返回相同的整数,前提是不修改{@code =}对象比较中使用的信息。此整数不需要保持一致,从一个应用程序的执行到另一个相同的应用程序的执行。如果两个对象根据{@code equals(Object)} 方法是相等的,那么在每个对象上调用{@code hashCode}方法必须产生相同的整数结果。根据{@link java.lang.Object#equals(java.lang.Object)} 方法,如果两个对象是不等的,那么在两个对象上调用{@code hashCode}方法必须产生不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同的整数结果可能会提高哈希表的性能。
hashcode存在的意义:
哈希算法也称为散列算法,是将数据依特定算法产生的结果直接指定到一个地址上。这个结果就是由 hashCode 方法产生。这样一来,当集合要添加新的元素时,先调用这个元素的 hashCode 方法,就一下子能定位到它应该放置的物理位置上。
①、如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;
②、如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了;
③、不相同的话,也就是发生了Hash key相同导致冲突的情况,那么就在这个Hash key的地方产生一个链表,将所有产生相同HashCode的对象放到这个单链表上去,串在一起(很少出现)。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。
hash冲突:
假设 A.hashCode() = B.hashCode() = 0x001,这个时候就产生了hash冲突,这时候由于最先是插入了 A,在插入的B的时候,我们发现 B 是要插入到 A 所在的位置,而 A 已经插入了,这时候就通过调用 equals 方法判断 A 和 B 是否相同,如果相同就不插入 B,如果不同则将 B 插入到 A 后面的位置。
这个时候我们可以得出一下结论:可以细细品一下
两个对象相等,其 hashCode 一定相同;
两个对象不相等,其 hashCode 有可能相同;
hashCode 相同的两个对象,不一定相等;
hashCode 不相同的两个对象,一定不相等;
java关键字 native
看了上面的Object方法,以前都没注意过native,忍不住上网搜了一下,并有了以下记录:
一般情况下,我们完全可以使用 Java 语言编写程序,但某些情况下,Java 可能会不满足应用程序的需求,或者是不能更好的满足需求,比如:
①、标准的 Java 类库不支持应用程序平台所需的平台相关功能。
②、我们已经用另一种语言编写了一个类库,如何用Java代码调用?
③、某些运行次数特别多的方法代码,为了加快性能,我们需要用更接近硬件的语言(比如汇编)编写。
上面这三种需求,其实说到底就是如何用 Java 代码调用不同语言编写的代码。那么 JNI 应运而生了。
从Java 1.1开始,Java Native Interface (JNI)标准就成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计 的,但是它并不妨碍你使用其他语言,只要调用约定受支持就可以了。使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的,比如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。
JNI 的缺点:
①、程序不再跨平台。要想跨平台,必须在不同的系统环境下重新编译本地语言部分。
②、程序不再是绝对安全的,本地代码的不当使用可能导致整个程序崩溃。一个通用规则是,你应该让本地方法集中在少数几个类当中。这样就降低了JAVA和C之间的耦合性。
而native
native 用来修饰方法,用 native 声明的方法表示告知 JVM 调用,该方法在外部定义,我们可以用任何语言去实现它。 简单地讲,一个native Method就是一个 Java 调用非 Java 代码的接口。
native 语法:
①、修饰方法的位置必须在返回类型之前,和其余的方法控制符前后关系不受限制。
②、不能用 abstract 修饰,也没有方法体,也没有左右大括号。
③、返回值可以是任意类型
toString()
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
getClass().getName()是返回对象的全类名(包含包名),Integer.toHexString(hashCode()) 是以16进制无符号整数形式返回此哈希码的字符串表示形式。
还有notify()/notifyAll()/wait() finalize 方法 registerNatives 方法
…等等会在后面提到
String家族
主要变量:
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
public static final Comparator<String> CASE_INSENSITIVE_ORDER
= new CaseInsensitiveComparator();
hashcode()
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;
}
equals方法:
/**
* String的equals方法,重写了Object的equals方法(区分大小写)
* 比较的是两个字符串的值是否相等
* 参数是一个Object对象,而不是一个String对象。这是因为重写的是Object的equals方法,所以是Object
* 如果是String自己独有的方法,则可以传入String对象,不用多此一举
*/
public boolean equals(Object anObject) {
if (this == anObject) { //首先判断形参str2是否跟当前对象str1是同一个对象,既比较地址是否相等
return true; //如果地址相等,那么自然值也相等,毕竟是同一个字符串对象
}
if (anObject instanceof String) { //判断str2对象是否是一个String类型,过滤掉非String类型的比较
String anotherString = (String)anObject; //如果是String类型,转换为String类型
int n = value.length; //获得当前对象str1的长度
if (n == anotherString.value.length) { //比较str1的长度和str2的长度是否相等
//如是进入核心算法
char v1[] = value; //v1为当前对象str1的值,v2为参数对象str2的值
char v2[] = anotherString.value;
int i = 0; //就类似于for的int i =0的作用,因为这里使用while
while (n-- != 0) { //每次循环长度-1,直到长度消耗完,循环结束
if (v1[i] != v2[i]) //同索引位置的字符元素逐一比较
return false; //只要有一个不相等,则返回false
i++;
}
return true; //如比较期间没有问题,则说明相等,返回true
}
}
return false;
}
/**
* 这也是一个String的equals方法,与上一个方法不用,该方法(不区分大小写),从名字也能看出来
* 是对String的equals方法的补充。
* 这里参数这是一个String对象,而不是Object了,因为这是String本身的方法,不是重写谁的方法
*/
public boolean equalsIgnoreCase(String anotherString) {
return (this == anotherString) ? true //一样,先判断是否为同一个对象
: (anotherString != null)
&& (anotherString.value.length == value.length) //再判断长度是否相等
&& regionMatches(true, 0, anotherString, 0, value.length); //再执行regionMatchs方法
}
/**
* 这是一个公有的比较方法,参数是StringBuffer类型
* 实际调用的是contentEquals(CharSequence cs)方法,可以说是StringBuffer的特供版
*/
public boolean contentEquals(StringBuffer sb) {
return contentEquals((CharSequence)sb);
}
/**
* 这是一个私有方法,特供给比较StringBuffer和StringBuilder使用的。
* 比如在contentEquals方法中使用,参数是AbstractStringBuilder抽象类的子类
*
*/
private boolean nonSyncContentEquals(AbstractStringBuilder sb) {
char v1[] = value; //当前String对象的值
char v2[] = sb.getValue(); //AbstractStringBuilder子类对象的值
int n = v1.length; //后面就不说了,其实跟equals方法是一样的,只是少了一些判断
if (n != sb.length()) {
return false;
}
for (int i = 0; i < n; i++) {
if (v1[i] != v2[i]) {
return false;
}
}
return true;
}
/**
* 这是一个常用于String对象跟StringBuffer和StringBuilder比较的方法
* 参数是StringBuffer或StringBuilder或String或CharSequence
* StringBuffer和StringBuilder和String都实现了CharSequence接口
*/
public boolean contentEquals(CharSequence cs) {
// Argument is a StringBuffer, StringBuilder
if (cs instanceof AbstractStringBuilder) { //如果是AbstractStringBuilder抽象类或其子类
if (cs instanceof StringBuffer) { //如果是StringBuffer类型,进入同步块
synchronized(cs) {
return nonSyncContentEquals((AbstractStringBuilder)cs);
}
} else { //如果是StringBuilder类型,则进入非同步块
return nonSyncContentEquals((AbstractStringBuilder)cs);
}
}
/***下面就是String和CharSequence类型的比较算法*****/
// 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;
}
Java String类为什么是final的?
1.为了实现字符串池
2.为了线程安全(多线程访问)
3.为了实现String可以创建HashCode不可变性
若 String允许被继承, 由于它的高度被使用率, 可能会降低程序的性能,所以String被定义成final。
String对象是真的无法修改吗?
答案是否定的,还是有途径修改的,那就是通过反射。
因为final的是字符数组,只代表这个value变量的引用不能改变,不代表value指向的对象不可以改变,String只是没有提供修改的途径,即使有也会是不对外公开。但数组中的元素是可以修改的,虽然没有途径,但还是反射就是一个逆天的存在,我们可以通过反射的途径去修改字符数组value的元素的值。
String str="abc";
System.out.println("修改前:"+str);
Field valueField=str.getClass().getDeclaredField("value");
valueField.setAccessible(true);
char[] temp=(char[])valueField.get(str);
temp[0]='1';
temp[1]='2';
temp[2]='3';
System.out.println("修改后:"+str);
String类和CharSequencee接口的关系
CharSequence是一个接口,它只包括length(), charAt(int index), subSequence(int start, int end)这几个API接口。继承了CharSequence代表着这是一个有序的字符集合。
String类length与codePointCount的区别
对于普通字符串,这两种方法得到的值是一样的,但对于UniCode编码来说,还是有一点区别。
区别:
length()方法返回的是使用的是UTF-16编码的字符代码单元数量,不一定是实际上我们认为的字符个数。codePointCount()方法返回的是代码点个数,是实际上的字符个数。
例如:
String str = “/uD835/uDD6B”,那么机器会识别它是2个代码单元代理的1个代码点”Z“,故而,length的结果是代码单元数量2,而codePointCount()的结果是代码点数量1.为什么String已经有一个compareTo方法了,还需要一个静态内部类再实现compare?
这里有一个疑惑,在String中已经有了一个compareTo的方法,为什么还要有一个CaseInsensitiveComparator的内部静态类呢?网上的回答大多数都是说是为了代码复用,我觉得这是有道理的。
首先是一个代码复用的问题,因为String类的compareToIgnoreCase方法实际调用的也是静态内部的compare方法
其次这里采用了单例模式
个人想法:
我个人觉得,为什么要有个静态内部类呢,首先我们要区别comparable和comparator接口的区别。实现了comparable接口的类,我们就能够本类的内部重写compareTo方法实现大小比较,从而给Collections.sort或Arrays.sort进行排序。而如果一个类没有实现comparable接口也想该类可以排序怎么办,那么就可以创造一个外部比较器去实现comparator接口,重写compare方法。以达到可以给Collections和Arrays使用。
具体区别参考如下链接:
Comparator和Comparable接口的区别
因为String继承了comparable接口,所以可以重写compareTo(String str)方法,注意参数是String类型的。里面实现的方式不忽略大小写的比较方式。如果我们还需要一个可以忽视大小写的比较方法怎么办?我们可能立马想到的就是重载一个compareTo方法,多对个参数?问题是不行的,工具类只认compareTo(String str),那么我们此时要怎么办呢?所以只能求救于Comparetor接口。要用Comparetor接口必须创造一个比较器去实现Comparator接口,并重写compare方法。所以就有了String内部就有了实现Comparator接口的静态内部类,这个静态内部类就是一个比较器。(当然这个静态内部类也可以不放进String类里。可以单独作为一个类,可是没有必要,因为这个类也就在String内部使用,所以作为静态内部类即可。)
StringBuffer&StringBuilder
这三个类之间的区别主要是在两个方面,即运行速度和线程安全这两方面。
- 首先说运行速度,或者说是执行速度,在这方面运行速度快慢为:StringBuilder > StringBuffer > String
String最慢的原因:
**String为字符串常量,而StringBuilder和StringBuffer均为字符串变量,即String对象一旦创建之后该对象是不可更改的,但后两者的对象是变量,是可以更改的。**以下面一段代码为例:
1 String str="abc";
2 System.out.println(str);
3 str=str+"de";
4 System.out.println(str);
如果运行这段代码会发现先输出“abc”,然后又输出“abcde”,好像是str这个对象被更改了,其实,这只是一种假象罢了,JVM对于这几行代码是这样处理的,首先创建一个String对象str,并把“abc”赋值给str,然后在第三行中,其实JVM又创建了一个新的对象也名为str,然后再把原来的str的值和“de”加起来再赋值给新的str,而原来的str就会被JVM的垃圾回收机制(GC)给回收掉了,所以,str实际上并没有被更改,也就是前面说的String对象一旦创建之后就不可更改了。所以,Java中对String对象进行的操作实际上是一个不断创建新的对象并且将旧的对象回收的一个过程,所以执行速度很慢。
而StringBuilder和StringBuffer的对象是变量,对变量进行操作就是直接对该对象进行更改,而不进行创建和回收的操作,所以速度要比String快很多。
另外,有时候我们会这样对字符串进行赋值
1 String str="abc"+"de";
2 StringBuilder stringBuilder=new StringBuilder().append("abc").append("de");
3 System.out.println(str);
4 System.out.println(stringBuilder.toString());
这样输出结果也是“abcde”和“abcde”,但是String的速度却比StringBuilder的反应速度要快很多,这是因为第1行中的操作和
String str=“abcde”;
是完全一样的,所以会很快,而如果写成下面这种形式
1 String str1="abc";
2 String str2="de";
3 String str=str1+str2;
那么JVM就会像上面说的那样,不断的创建、回收对象来进行这个操作了。速度就会很慢。
2. 再来说线程安全
在线程安全上,StringBuilder是线程不安全的,而StringBuffer是线程安全的
如果一个StringBuffer对象在字符串缓冲区被多个线程使用时,StringBuffer中很多方法可以带有synchronized关键字,所以可以保证线程是安全的,但StringBuilder的方法则没有该关键字,所以不能保证线程安全,有可能会出现一些错误的操作。所以如果要进行的操作是多线程的,那么就要使用StringBuffer,但是在单线程的情况下,还是建议使用速度比较快的StringBuilder。
3. 总结一下
String:适用于少量的字符串操作的情况
StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况
StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况
StringBuffer
类描述:
一个线程安全的可变字符序列。字符串缓冲区类似于{@link string},但是可以修改。在任何它包含一些特定的字符序列,但是长度和内容的序列可以通过一定的改变方法调用。
字符串缓冲区是安全的使用多个线程的方法在必要时同步,以便所有操作都可以在任何特定实例的行为就好像它们以某种串行顺序出现一样这与每个方法调用的顺序一致涉及的各个线程。对{@code StringBuffer}的主要操作是{@code append}和{@code insert}方法,它们是重载,以便接受任何类型的数据。每一个有效将给定的数据转换为字符串,然后追加或入
字符串的字符到字符串缓冲区。 {@code append}方法总是在末尾添加这些字符缓冲区;{@code insert}方法将字符添加到一个指定的点。例如,如果{@code z}引用一个字符串缓冲对象当前内容为{@code “start”},则方法调用{@code z.append(“le”)}将导致字符串缓冲区中包含{@code “惊吓”},反之{@code z。insert(4, “le”)}将把字符串缓冲区改为包含{@code “starlet”}。通常,如果sb引用一个{@code StringBuffer}的实例,则{@code sb.append(x)}与
{@code sb.insert(sb.length(), x)}。每当涉及源序列的操作发生时(例如从源序列追加或插入),该类同步只在执行操作的字符串缓冲区上执行,而不在源上执行。注意,虽然{@code StringBuffer}被设计成可以安全使用如果构造函数或{@code追加}或{@code插入}操作被传递一个源序列在线程之间共享,调用代码必须确保操作对源具有一致且不变的视图
操作期间的顺序。这可以通过调用者在过程中持有锁来满足操作的调用,通过使用不可变的源序列,或由非跨线程共享源序列。
每个字符串缓冲区都有一个容量。只要长度的字符串缓冲区中包含的字符序列不超过容量,无需重新分配内部
缓冲数组。如果内部缓冲区溢出,则为自动放大。除非另有说明,否则将一个{@code null}参数传递给构造函数或方法将导致{@link NullPointerException}抛出。在JDK 5发布时,这个类已经被一个等价的类补充了为单个线程设计的类,{@link StringBuilder}。的通常应该优先使用 {@code StringBuilder}类这个,因为它支持所有相同的操作,但它快,因为它不执行同步。
StiringBuffer
跟StringBuilder
类似,只不过为了实现同步,很多方法使用lSynchronized
修饰,如下面的方法:
public synchronized int length() {
return count;
}
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
public synchronized void setLength(int newLength) {
toStringCache = null;
super.setLength(newLength);
}
可以看到,方法前面确实加了Synchronized
。
另外,在上面的append()
以及setLength()
方法里面还有个变量toStringCache
。这个变量是用于最近一次toString()
方法的缓存,任何时候只要StringBuffer
被修改了这个变量会被赋值为null
。StringBuffer
的toString
如下:
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}
在这个方法中,如果toStringCache
为null
则先缓存。最终返回的String
对象有点不同,这个构造方法还有个参数true
。找到String
的源码看一下:
String(char[] value, boolean share) {
// assert share : "unshared not supported";
this.value = value;
}
原来这个构造方法构造出来的String
对象并没有实际复制字符串,只是把value
指向了构造参数,这是为了节省复制元素的时间。不过这个构造器是具有包访问权限,一般情况下是不能调用的。
StringBuilder
一个可变的字符序列。这个类提供了一个与{@code StringBuffer}兼容的API ,但不保证同步。
这个类被设计用来替代{@code StringBuffer}在字符串缓冲区被单个线程使用的地方(通常是这种情况)。
在可能的情况下,建议优先使用这个类,而不是 {@code StringBuffer},因为它在大多数实现中会更快。
{@code StringBuilder}上的主要操作是 {@code append}和{@code insert}方法,它们被重载以接受任何类型的数据。每个方法都有效地将给定的数据转换为字符串,然后将该字符串的字符附加或插入到字符串生成器中。
{@code append}方法总是在生成器的末尾处添加这些字符;
{@code insert}方法在指定的点添加字符。例如,如果{@code z}引用当前内容为“{@code start}”的string builder对象,则方法调用{@code z.append(“le”)}将导致string builder包含“{@code startle}”,而 {@code insert(4,“le”)}将把字符串生成器更改为包含“{@code starlet}”。
一般情况下,如果sb引用一个{@code StringBuilder}的实例,则{@code sb.append(x)}与{@code sb.insert(sb.length(), x)}具有相同的效果。
每个字符串生成器都有一个容量。只要字符串生成器中包含的字符序列的长度不超过容量,就没有必要分配新的内部缓冲区。如果内部缓冲区溢出,则自动将其增大。
{@code StringBuilder}的>实例对于被多个线程使用是不安全的。如果需要这样的同步,那么建议使用{@link java.lang。StringBuffer}。除非另有说明,否则将{@code null}参数传递给构造函数或该类中的方法将导致{@link NullPointerException}被抛出。
AbstractStringBuilder (StringBuffer和StringBuilder均继承自这个类)
实现一个可修改的字符串。在任何时候它都包含一些特殊的字符序列,但长度和内容的序列可以通过某些方法调用来改变。
扩容
public void ensureCapacity(int minimumCapacity) {
if (minimumCapacity > 0)
ensureCapacityInternal(minimumCapacity);
}
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0)
expandCapacity(minimumCapacity);
}
void expandCapacity(int minimumCapacity) {
int newCapacity = value.length * 2 + 2;
if (newCapacity - minimumCapacity < 0)
newCapacity = minimumCapacity;
if (newCapacity < 0) {
if (minimumCapacity < 0) // overflow
throw new OutOfMemoryError();
newCapacity = Integer.MAX_VALUE;
}
value = Arrays.copyOf(value, newCapacity);
}
扩容的方法最终是由expandCapacity()
实现的,在这个方法中首先把容量扩大为原来的容量加2,如果此时仍小于指定的容量,那么就把新的容量设为minimumCapacity
。然后判断是否溢出,如果溢出了,把容量设为Integer.MAX_VALUE
。最后把value
值进行拷贝,这显然是耗时操作。
append()方法
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
append()
是最常用的方法,它有很多形式的重载。上面是其中一种,用于追加字符串。如果str
是null
,则会调用appendNull()
方法。这个方法其实是追加了'n'
、'u'
、'l'
、'l'
这几个字符。如果不是null
,则首先扩容,然后调用String
的getChars()
方法将str
追加到value
末尾。最后返回对象本身,所以append()
可以连续调用。
reverse()方法
内部调换顺序,返回的仍是自己
public AbstractStringBuilder reverse() {
boolean hasSurrogates = false;
int n = count - 1;
for (int j = (n-1) >> 1; j >= 0; j--) {
int k = n - j;
char cj = value[j];
char ck = value[k];
value[j] = ck;
value[k] = cj;
if (Character.isSurrogate(cj) ||
Character.isSurrogate(ck)) {
hasSurrogates = true;
}
}
if (hasSurrogates) {
reverseAllValidSurrogatePairs();
}
return this;
}
Attention!!!
StringBuilder stringBuilder = new StringBuilder("1121");
StringBuilder reverse = stringBuilder.reverse();
System.out.println(reverse==stringBuilder); //true
System.out.println(reverse.equals(stringBuilder)); //true
/
String str="111";
System.out.println(str.equals(reverse)); //false
System.out.println(reverse.equals(str)); //false
1.reverse.equals
public boolean equals(Object obj) {
return (this == obj);
}
2.str.equals
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
//anObject isnot instanceof String
}
return false;
3.stringBuilder.reverse()
返回的是和StringBuilder相同的对象
即 stringBuilder.reverse();
sout stringBuilder //1211
Boolean
类申明:
public final class Boolean implements java.io.Serializable,
Comparable<Boolean>
Java的Boolean类主要作用就是对基本类型boolean进行封装,提供了一些处理boolean类型的方法,比如String类型和boolean类型的转换。
在Java的世界中只要全局存在两个Boolean对象即可,实例化出多余的Boolean对象仍然能正确表示布尔值,只是会浪费一些空间和影响时间性能。
public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
public static Boolean valueOf(String s) {
return toBoolean(s) ? TRUE : FALSE;
}
当我们要把一个字符串(例如:“true”,“false”)转为Boolean类的实例时,或者把数据类型为boolean的数据转为Boolean类的实例时,不需要再通过:new Boolean(true)的方式重新创建一个实例,这样做的好处可以节省内存。虽然实例化一个Boolean对象消耗的内存不多。
Boolean的TYPE属性,它toString的值其实是boolean
。
public static final Class<Boolean> TYPE = (Class<Boolean>)Class.getPrimitiveClass("boolean");
System.out.println(Boolean.TYPE);
//boolean
Java_java_lang_Class_getPrimitiveClass(JNIEnv *env,
jclass cls,
jstring name)
{
const char *utfName;
jclass result;
if (name == NULL) {
JNU_ThrowNullPointerException(env, 0);
return NULL;
}
utfName = (*env)->GetStringUTFChars(env, name, 0);
if (utfName == 0)
return NULL;
result = JVM_FindPrimitiveClass(env, utfName);
(*env)->ReleaseStringUTFChars(env, name, utfName);
return result;
}
当TYPE执行toString时,逻辑如下,则其实是getName函数决定其值,getName通过native方法getName0从JVM层获取名称,
public String toString() {
return (isInterface() ? "interface " : (isPrimitive() ? "" : "class "))
+ getName();
}
hashcode()
可以看出 true的hashcode 已经定死了1231,false的hashcode为1237;其代表Boolean的位置在jvm的位置是固定的,具体在哪儿,jvm我还没看.
public static int hashCode(boolean value) {
return value ? 1231 : 1237;
}
equals()
public boolean equals(Object obj) {
if (obj instanceof Boolean) {
//如果obj为Booleab的实例,那么久
return value == ((Boolean)obj).booleanValue();
}
return false;
}
剩余的一些方法:
public static boolean logicalAnd(boolean a, boolean b) {
return a && b;
}
public static boolean logicalOr(boolean a, boolean b) {
return a || b;
}
public static boolean logicalXor(boolean a, boolean b) {
return a ^ b;
}
public static int compare(boolean x, boolean y) {
return (x == y) ? 0 : (x ? 1 : -1);
}
public static boolean parseBoolean(String s) {
return ((s != null) && s.equalsIgnoreCase("true"));
}
public boolean booleanValue() {
return value;
}
Byte
类声明:
public final class Byte extends Number implements Comparable<Byte>
接下来看看Number类里是什么东西?
public abstract class Number implements java.io.Serializable{
intValue
longValue
floatValue
doubleValue
byteValue
shortValue
}
抽象类 Number是平台的超类类表示可转换为的数值;
接着来看Byte类的字段:
private final byte value; // 包装的 byte 值
public static final byte MIN_VALUE = -128; // 最小值是 -128
public static final byte MAX_VALUE = 127; // 最大值是 127
public static final Class<Byte> TYPE = (Class<Byte>) Class.getPrimitiveClass("byte"); //返回byte
public static final int SIZE = 8; // byte 占 8 bits
public static final int BYTES = SIZE / Byte.SIZE; // byte 占一个字节
private static final long serialVersionUID = -7183698231559129828L;
构造函数:
public Byte(byte value) {
this.value = value;
}
public Byte(String s) throws NumberFormatException {
this.value = parseByte(s, 10);
}
toString()
public static String toString(byte b) {
return Integer.toString((int)b, 10);
}
parseByte()
将字符串参数解析为带符号的十进制{@code字节}。字符串中的字符必须都是十进制数字,
除了第一个字符可能是ASCII减号
public static byte parseByte(String s, int radix)
throws NumberFormatException {
int i = Integer.parseInt(s, radix);
if (i < MIN_VALUE || i > MAX_VALUE)
throw new NumberFormatException(
"Value out of range. Value:\"" + s + "\" Radix:" + radix);
return (byte)i;
}
valueOf()
Byte.valueOf(20,4)就是四进制表示是20,转换成十进制是多少呢——显然是8
radix 表示你要转化的那个进程的那个基数
public static Byte valueOf(String s, int radix)
throws NumberFormatException {
return valueOf(parseByte(s, radix));
}
public static Byte valueOf(byte b) {
final int offset = 128;
return ByteCache.cache[(int)b + offset];
}
ByteCache()
Byte实现了ByteCache这一静态内部类,不看代码也能猜测出此类由于缓存Byte数据,果不其然,cache[]存储了-128127之间的所有数值。在使用valueOf获取Byte实例对象时都是获取cache[]中的对象(多次获取的都是同一个对象呀),如获取-128,即cache[-128+128]=cache[0],使用offset这个技巧很巧妙呀!
private static class ByteCache {
private ByteCache(){}
// 最小值到最大值 加1(中间的0)
static final Byte cache[] = new Byte[-(-128) + 127 + 1];
static {
for(int i = 0; i < cache.length; i++)
cache[i] = new Byte((byte)(i - 128));
}
}
// 初始化之后 就可以从缓存中取值,改方法在类中有大量复用
public static Byte valueOf(byte b) {
final int offset = 128;
return ByteCache.cache[(int)b + offset];
}
Double
类声明:
public final class Double extends Number implements Comparable<Double>
字段:
public static final double POSITIVE_INFINITY = 1.0 / 0.0;
public static final double NEGATIVE_INFINITY = -1.0 / 0.0;
public static final double NaN = 0.0d / 0.0;
public static final double MAX_VALUE = 0x1.fffffffffffffP+1023; // 1.7976931348623157e+308
public static final double MIN_NORMAL = 0x1.0p-1022; // 2.2250738585072014E-308
public static final double MIN_VALUE = 0x0.0000000000001P-1022; // 4.9e-324
public static final int MAX_EXPONENT = 1023;
public static final int MIN_EXPONENT = -1022;
public static final int SIZE = 64;
public static final int BYTES = SIZE / Byte.SIZE;
Integer
待续
集合
Set
先来看一个东西:
public static void main(String[] args) {
LinkedHashSet<Long> set = new LinkedHashSet<Long>();
set.add(1L);
set.add(2L);
set.add(3L);
while (set.iterator().hasNext()) {
Long value = set.iterator().next();
System.out.println(value);
}//1无限输出,原因是每次都创建了一个新的迭代器,就差不多是临时变量的问题
Iterator<Long> iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
Iterator<Long> iterator2 = set.iterator();
while (iterator2.hasNext()){
System.out.println(iterator2.next());
}
//iterator只是遍历set ,并不会修改set的内的东西
}
Integer
int MIN_EXPONENT = -1022;
public static final int SIZE = 64;
public static final int BYTES = SIZE / Byte.SIZE;
集合
Set
先来看一个东西:
public static void main(String[] args) {
LinkedHashSet<Long> set = new LinkedHashSet<Long>();
set.add(1L);
set.add(2L);
set.add(3L);
while (set.iterator().hasNext()) {
Long value = set.iterator().next();
System.out.println(value);
}//1无限输出,原因是每次都创建了一个新的迭代器,就差不多是临时变量的问题
Iterator<Long> iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
Iterator<Long> iterator2 = set.iterator();
while (iterator2.hasNext()){
System.out.println(iterator2.next());
}
//iterator只是遍历set ,并不会修改set的内的东西
}
未完待续