1.2、java.lang(精读部分)

我们都知道java.lang是java的核心包。
现在我们开始阅读lang包的一些东西。

文章目录

  • Object

            • 《8种基本数据类型 》
  • byte
  • short
  • Integer
  • long
  • boolean
  • Character
  • Double
  • Float

  • String
  • StringBuffer
  • StringBuilder
  • Math
  • Enum

Object

Object类是java中所有类的父类(超类),所有的对象(包括数组)都实现了这个类的方法

构造方法只有一个默认的无参构造方法

接下来,我们一个个,看看其中的方法;

1、clone

protected native Object clone() throws CloneNotSupportedException;

如果类没有实现Cloneable接口。 就算重写了clone方法的子类也可以抛出一个异常以指示实例无法克隆。 (CloneNotSupportedException

所以的数组是默认实现了Cloneable接口的

以ArrayList为例,下面的一般情况下,三个都会输出true;

ArrayList<String> x = new ArrayList<>();
System.out.println(x.clone().getClass() == x.getClass());
System.out.println(x.clone() != x);
System.out.println(x.clone().equals(x));

虽说,clone方法是这个实例的一个克隆。 但是,可以看到,他们并不是完全相等的。(第二个输出)

2、equals

public boolean equals(Object obj)

equals方法在非空对象引用上实现的等价关系有:自反性;对称性;传递性;

对于任何非空的x,x.equals(null)应该返回false
对于任何非空的x和y ,当且仅当x和y引用相同的对象( x == y具有值true )时,该方法返回true 。

请注意,无论何时重写该方法,通常需要重写hashCode方法,以便维护hashCode方法的通用合同,该方法规定相等的对象必须具有相等的哈希码。

3、getClass
jdk中描述:getClass方法返回此Object的运行时类。 返回的类对象是被表示类的static synchronized方法锁定的对象。

何谓运行时类,即内存中实实在在存在的类

这个方法一般在 反射 中应用的比较多。

4、其他一些方法

返回值 方法 描述
int hashCode() 返回对象的哈希码值。
void notify() 唤醒正在等待对象监视器的单个线程。
void notifyAll() 唤醒正在等待对象监视器的所有线程。
String toString() 返回对象的字符串表示形式。
void wait() 导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法。
void wait(long timeout) 导致当前线程等待,直到另一个线程调用 notify()方法或该对象的 notifyAll()方法,或者指定的时间已过。
void wait(long timeout, int nanos) 导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法,或者某些其他线程中断当前线程,或一定量的实时时间。


《8种基本数据类型 》

byte

1.2、java.lang(精读部分)_第1张图片
byte类是final类。也就是说它是不可以被继承的

还有就是经典的“127和128”的问题了(图懒得换了…MAX_VALUE = 127

java中,byte是一个字节占八位。

127 ( 01111111 ) 127(01111111) 12701111111)
在加一,出现什么情况?计组原理中说过第一位是符号位(0正1负)
1.2、java.lang(精读部分)_第2张图片
第一个:int+byte;
第二个:byte+byte(+=的特性)越界:

在计算机中,由于原码的表示不便,进行运算需要判断其正负极其绝对值的大小。所以,在计算机中,通常都是采用补码形式。 正整数的补码与原码形式相同,正数的原码反码补码相同,负数的补码=反码(反码=原码的符号位不变其他位取反)+1

127 ( 01111111 ) 127(01111111) 12701111111)+ 1 ( 00000001 ) 1(00000001) 100000001)得到 10000000 10000000 10000000

10000000 10000000 10000000,按照计算,这个不应该是-0吗?为什么是-128呢?

首先灌输一个新的概念叫,模
什么是“模”,想象日常使用的钟表,它可以显示0~12点的时间,假设现在是2点钟,请用手动拨动时针的方式将时间减4小时,你会怎么做?

有两种方式:
逆时针将时针拨4小时,
顺时针将时针拨8(12-4)小时

这里要讲的是第二种方式,为什么顺时针拨12-4也可以达到和正常思维的第一种方式一样的位置。12就是模。同样的,如果是十进制的两位数,80-10 和 80+90在不考虑百位数的基础上都是70。这里的90就是100-10得来的,这种情况下100就是模模就好比是一个极限,在它的范围内,两个相加等于模的数互为补数,还是举100的例子90和10, 55和45,68和32,互为补数在模的范围内做减法,可以将“X-Y”的减法变更为“X+Y的补数“的加法,当然前提是不考虑百位数

思考题,上面举的例子是大数减小数,那么如果是小数减大数会怎么样呢?如果是10-80,结果应该是-70,但如果按照10+(100-80),结果是30。而很明显-70和30不是一回事,这里也没有百位数的问题,这种情况应该怎么破?

当初的那些先贤们想出来的办法很简单,就是把这两个数直接划上等号,正好顺便解决了负数的表达方式。再来仔细看看这两个数的关系:-70绝对值的补数就正好是30所以在计算机中,负数的表达方式就是它绝对值的补数但是问题又来了,看起来这个解决方式很完美了,但别忘了,30他已经代表了正数的30了,现在又要用来代表负数的-70,谁知道它出现的时候到底是代表哪个数?

为了解决这个问题,需要给这套规则划定一个范围,原来是0~99的正数,现在既然要用部分正数来代替负数了,那就要规定一个范围来使得一个数只代表一个含义,正好一人一半,0~49这个区间就代表正数,50~99的区间就用来代表各自补数的负值,例:98就代表 -2

第三步,现在回到二进制的计算机世界8位二进制数一共可以表示2的8次方,256个数,即0~255 (别忘了0也要占一位的),他们的极限就是256,即256是8位二进制数的模 ,应该不难理解吧,同上十进制的两位数0~99的模是100。还是用二进制来说明清楚,8位二进制能表示的数的极限是1 1 1 1 1 1 1 1, 就是255,在这基础上加0 0 0 0 0 0 0 1,出现了进一位 即 1 0 0 0 0 0 0 0 0这个1 0 0 0 0 0 0 0 0就是8位二进制数的模,256同样按照第二步讲的逻辑,一半的数0~127,代表其正数本身,另一半的数 128~255,代表其补数的负值,即“-1~-128”的区间。 而 “X-Y”的减法 就用 “X+Y的补数” 的加法来表示,完美! 唯一需要注意的事情是任何计算的输入值和输出结果值都需要严格遵守-128~127的范围,一旦溢出就会报错。这也就是我们在编程里强调的为什么 byte+byte还得是byte,int+int还得是int,数据溢出问题也是每一个程序员都需要注意的问题。这样一说是不是可以理解-128的补码是怎么来的了吧? 他就是256-|-128|=128二进制的128是不是就是1 0 0 0 0 0 0 0 ?

最终问题,那书和老师为什么要用原码,反码来讲补码 ?空穴来风,未必无因那是因为计算机就是这样求负数的补码的,我们在键盘上敲一个负数的时候,计算机要把它用补码的形式存储下来,还记得上面我们讲的补码是怎么来的吗?模-绝对值,这是不是个减法公式?但计算机没有减法逻辑,我们费了那么大的劲搞了一套补码的规则就是为了用加法来替代减法,但为了实现这么套规则,却跨不过一个坎,就是把负数计算成补码仍然是需要减法逻辑的。怎么办呢,那些伟大的先贤们 (膜拜)就想出了这么个办法:首位不变,其余位取反后,再加一

参考自:在8位二进制中,-128 没有原码、反码形式,那么它的补码是怎么计算出来的?还是约定的? - Simon Cao的回答 - 知乎

别忘了,object类是所有类的超类,所以,byte中还重写了hashcode,tostring等方法。

再看看下面的例子
1.2、java.lang(精读部分)_第3张图片
int型是32位的,前面还要补23个0,然后取后八位的结果是00000000
1.2、java.lang(精读部分)_第4张图片
当a=257的时候,输出就会是1,因为100000001,取后八位。我这样理解,其实,是一个新的循环,类似于上面的模的概念。

那么 256+128~256+256之间的数应该是负数
1.2、java.lang(精读部分)_第5张图片
思考:400去掉一个256等于144,0~127为正,128~255为负数,144-256(模)=-112

使用byte时,一定要主要,值得范围 [-128,127]

short

java中short是2个字节,16位,所以, 2 16 / 2 = 32768 2^{16}/2=32768 216/2=32768,所以,1.2、java.lang(精读部分)_第6张图片

Integer

Integer类是基本数据类型int的包装器类,是抽象类Number的子类,位于java.lang包中。

Integer类在对象中包装了一个基本类型int的值,也就是每个Integer对象包含一个int类型的字段。

在这里插入图片描述
一些静态字段:

[static int] MAX_VALUE:值为 231-1 的常量,它表示 int 类型能够表示的最大值。2147483647

[static int] MIN_VALUE:值为 -231 的常量,它表示 int 类型能够表示的最小值。-2147483648

[static int] SIZE: 用来以二进制补码形式表示 int 值的比特位数。

[static Class] TYPE:表示基本类型 int 的 Class 实例。

[static int] BYTES:返回int值所占的字节数。

equals方法

public boolean equals(Object obj) {
   if (obj instanceof Integer) {
        return value == ((Integer)obj).intValue();
    }
    return false;
}

在Integer类中,“==”用来比较对象地址是否相同,并且Integer类重写了equals(Object obj)方法,在equals(Object obj)方法中,会先判断参数中的对象obj是否是Integer类型的对象,如果是则判断值是否相同,值相同则返回true,值不同则返回false,如果obj不是Integer类的对象,则返回false。需要注意的是:当参数是基本类型int时,编译器会给int自动装箱成Integer类,然后再进行比较。

toString方法

public static String toString(int i) {
    if (i == Integer.MIN_VALUE)
        return "-2147483648";
    int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
    char[] buf = new char[size];
    getChars(i, size, buf);
    return new String(buf, true);
}

//其中stringSize和getChars如下:
static int stringSize(int x) {
    for (int i=0; ; i++)
        if (x <= sizeTable[i])
            return i+1;
}

static void getChars(int i, int index, char[] buf) {
  int q, r;
    int charPos = index;
    char sign = 0;

    if (i < 0) {
        sign = '-';
        i = -i;
    }

    // Generate two digits per iteration
    while (i >= 65536) {
        q = i / 100;
    // really: r = i - (q * 100);
        r = i - ((q << 6) + (q << 5) + (q << 2));
        i = q;
        buf [--charPos] = DigitOnes[r];
        buf [--charPos] = DigitTens[r];
    }

    // Fall thru to fast mode for smaller numbers
    // assert(i <= 65536, i);
    for (;;) {
        q = (i * 52429) >>> (16+3);
        r = i - ((q << 3) + (q << 1));  // r = i-(q*10) ...
        buf [--charPos] = digits [r];
        i = q;
        if (i == 0) break;
    }
    if (sign != 0) {
        buf [--charPos] = sign;
    }
}

toString返回该Integer值得String对象

r = i - ((q << 3) + (q << 1)); // r = i-(q*10) ...

针对这行,做个解释:<<:左移运算(在数字没有溢出的前提下,对于正数和负数,左移一位都相当于乘以2的1次方,左移n位就相当于乘以2的n次方。),>>:右移运算(右移一位相当于除2,右移n位相当于除以2的n次方。)

移位运算符的效率是比普通的乘法(除法)效率高的

具体是怎么做的,是二进制数的转换移位,不再赘述

将字符串(不能含有其他字符,构造方法Integer(String s),也同理)转为数字的方法是:

public static int parseInt(String s) throws NumberFormatException {
 	return parseInt(s,10);
}
public Integer(String s) throws NumberFormatException {      		
	this.value = parseInt(s, 10);
}
//其中调用的是另外一个重载方法。给的默认radix为10。具体的自行查看
//可以看出,integer构造方法本质上和parseint方法是一样的。

1.2、java.lang(精读部分)_第7张图片

long

long类型的值范围-9223372036854775808 ,9223372036854775807

Long(String s)
parseLong(String s)
equals()
toString()

都是和integer有异曲同工之妙的

boolean

Boolean 类有以下两种构造形式:

Boolean(boolean boolValue);
Boolean(String boolString);

其中 boolValue 必须是 true 或 false(其他的编译会出错),boolString 仅含有 字符串 true(不区分大小写),那么新的 Boolean 对象为 true;否则为 false。

忽略大小写,在源码中也是有体现的:如下:
在这里插入图片描述在这里插入图片描述

Character

有一个简单的构造方法:
Character(char value) :构造一个新分配的 Character 对象,用以表示指定的 char 值。

Character 类在对象中包装一个基本类型 char 的值。Character 类型的对象包含类型为 char 的单个字段。

此外,该类提供了几种方法,以确定字符的类别(小写字母,数字,等等),并将字符从大写转换成小写,反之亦然。

Double

double的值范围:4.9E-324 ,1.7976931348623157E308
这里的思路也是从,类的定义>>字段>>构造方法>>其他方法
在这里插入图片描述
一些静态字段:
1.2、java.lang(精读部分)_第8张图片
构造方法

Double(double value)
构造一个新分配的 Double 对象,它表示基本的 double 参数。

Double(String s)
构造一个新分配的 Double 对象,表示用字符串表示的 double 类型的浮点值。

其他一些方法提供 double和String的互转,也提供了其他一些处理 double 时有用的常量和方法。

Float

和double类异曲同工。


String

基本介绍:

String代表字符串。 Java程序中的所有字符串文字(例如"abc" )都被实现为此类的实例。 String是类嗷,不是基本数据类型。

字符串不变; 它们的值在创建后不能被更改。 字符串缓冲区支持可变字符串。 因为String对象是不可变的,它们可以被共享。

String类包括用于检查序列的各个字符的方法,用于比较字符串,搜索字符串,提取子字符串以及创建将所有字符翻译为大写或小写的字符串的副本。

在这里插入图片描述
实现了序列化的接口和Comparable接口

Comparable接口:该接口对实现它的每个类的对象强加一个整体排序。 这个排序被称为类的自然排序 ,类的compareTo方法被称为其自然比较方法 。

一些静态变量:

/** The value is used for character storage. */
private final char value[];

/** Cache the hash code for the string */
private int hash; // Default to 0

/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -6849794470754667710L;

/**
* Class String is special cased within the Serialization Stream Protocol.
*
* A String instance is written into an ObjectOutputStream according to
* 
* Object Serialization Specification, Section 6.2, "Stream Elements"
*/
private static final ObjectStreamField[] serialPersistentFields =
new ObjectStreamField[0];

一些构造方法:
在这里插入图片描述1.2、java.lang(精读部分)_第9张图片
在这里插入图片描述

进一步理解:
private final char value[];,由这个变量我们可以看出,为什么String类的对象一旦创建就不可变更的原因就是,因为字符串都是通过这个final修饰的char数组保存的(在内存中其实是字符串常量池中),字符串的每一个字符都是char类型的,char是基本类型。

不知道有没有人对第二个构造方法的方法体有疑问?

public String(String original) {
    this.value = original.value;//怎么赋值的?
    this.hash = original.hash;
}

这里的参数“original”,他本身也是String类的一个对象,而original.value是,对象点属性的基本使用。original.value,实际上是创建一个String类的一个对象“original”的value,把这个value,赋值给String类的value

String str = “abc”;
相当于:
char data[] = {‘a’, ‘b’, ‘c’};
String str = new String(data);

一些常用的方法

endsWith(String suffix) 
测试此字符串是否以指定的后缀结尾。

concat(String str) 
将指定的字符串连接到该字符串的末尾。 

charAt(int index) 
返回 char指定索引处的值。

compareTo(String anotherString) 
按字典顺序比较两个字符串。 

contains(CharSequence s) 
当且仅当此字符串包含指定的char值序列时才返回trueequals(Object anObject) 
将此字符串与指定对象进行比较。 

equalsIgnoreCase(String anotherString) 
将此 String与其他 String比较,忽略案例注意事项。

indexOf(String str) 
返回指定子字符串第一次出现的字符串内的索引。 

isEmpty() 
返回 true如果,且仅当 length()0split(String regex) 
将此字符串分割为给定的 regular expression的匹配。 

length() 
返回此字符串的长度。 

substring(int beginIndex, int endIndex) 
返回一个字符串,该字符串是此字符串的子字符串。

trim() 
返回一个字符串,其值为此字符串,并删除任何前导和尾随空格。
//去掉所有的空白可以用replaceAll(" ","");

replaceAll(String regex, String replacement) 
用给定的替换替换与给定的 regular expression匹配的此字符串的每个子字符串。

StringBuffer

StringBuffer ,线程安全,可变的字符序列。 它的父类是AbstractStringBuilder

stringbuffer类
在这里插入图片描述
构造方法

    public StringBuffer() {
        super(16);
    }
    public StringBuffer(int capacity) {
        super(capacity);
    }
    public StringBuffer(String str) {
        super(str.length() + 16);
        append(str);
    }

//父类
    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }

注意value是没有用final修饰的。这也是字符串长度可变的原因
1.2、java.lang(精读部分)_第10张图片

注意value是没有用final修饰的。这也是字符串长度可变的原因

通过阅读源码知道:StringBuffer的字符串是通过调用父类的append方法,来把字符存起来的,而父类的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;
}

getchars方法,value就是之前贴出来的那个char数组
1.2、java.lang(精读部分)_第11张图片
return this 就是返回当前对象的引用(就是实际调用这个方法的实例化对象)

本例中,父类其实就返回了AbstractStringBuilder类的一个实例化对象
子类(代码如下)中返回的是StringBuffer 对象,而且我们可以观察到,这个append方法是加了关键字 synchronized,以此亦可以说明,stringbuff是线程安全的,其他所有的append方法都是有这个关键字的

@Override
 public synchronized StringBuffer append(String str) {
     toStringCache = null;
     super.append(str);
     return this;
 }

return this 的进一步理解测试

public class OCReading {
    private String a;
    public void setA(String str){
        this.a = str;
    }
    public String getA(){
        return this.a;
    }
    public OCReading get(String string){
        setA(string);
        return this;
    }
    public static void main(String[] args) {
        OCReading ocReading = new OCReading();
        OCReading ocReading1;
        ocReading1 = ocReading.get("sjt");
        System.out.println(ocReading1.getA());//out:sjt
    }
}

其他说明(摘自jdk1.8):

一般情况下,如果某人是指的一个实例StringBuffer ,则sb.append(x)具有相同的效果sb.insert(sb.length(), x) 。

每当涉及源序列(例如从源序列追加或插入)的操作发生时,该类仅在执行操作的字符串缓冲器上进行同步,而不在源上。 请注意,虽然StringBuffer被设计为可以安全地从多个线程并发使用,但如果构造函数或append或insert操作被传递通过线程共享的源序列,则调用代码必须确保该操作具有一致且不变的视图在操作期间的源序列。 呼叫者通过使用不可变的源序列,或者不跨线程共享源序列,可以在呼叫期间持有锁来满足这一点。

每个字符串缓冲区都有一个容量。 只要字符串缓冲区中包含的字符序列的长度不超过容量,就不必分配新的内部缓冲区数组。 如果内部缓冲区溢出,则会自动变大。

除非另有说明,否则将null参数传递给null中的构造函数或方法将导致抛出NullPointerException

StringBuilder

StringBuilder和stringbuffer大同小异,最重要的一个区别就是,stringbuffer线程安全(有锁),stringbuilder没有锁,是线程不安全的

Math

Math类包含执行基本数字运算的方法,如基本指数,对数,平方根和三角函数。

Math是final类。

两个基本属性和一个构造方法

 /**
     * Don't let anyone instantiate this class.
     */
    private Math() {}

    /**
     * The {@code double} value that is closer than any other to
     * e, the base of the natural logarithms.
     */
    public static final double E = 2.7182818284590452354;

    /**
     * The {@code double} value that is closer than any other to
     * pi, the ratio of the circumference of a circle to its
     * diameter.
     */
    public static final double PI = 3.14159265358979323846;

E是自然对数的基数,PI是圆周率(圆周长与其直径的比率)

这个类的方法基本上是和数学相关的计算:

  • 绝对值 abs
  • 正弦,余弦,正切,还有他们三个的反值
  • 对数,指数
  • 比较大小,计算 和 积 差 商
  • 获取随机数,四舍五入

Enum

枚举类:Java语言中所有枚举类型的公共基类,枚举类型是Java 5中新增特性的一部分,它是一种特殊的数据类型,之所以特殊是因为它既是一种类(class)类型却又比类类型多了些特殊的约束。

唯一的一个构造方法:

我们无法调用此构造函数。 它由编译器响应枚举类型声明发出的代码使用。

我们自己写的枚举类,编译器在编译的时候回生成对应的final类来继承该基类。

name - - 此枚举常量的名称,它是用于声明它的标识符。
ordinal - - 这个枚举常数的序数(它在枚举声明中的位置,其中初始常数被分配为零的序数)。

protected Enum(String name, int ordinal) {
    this.name = name;
    this.ordinal = ordinal;
}

在这里插入图片描述

更多请参考大佬博文:https://blog.csdn.net/javazejian/article/details/71333103

你可能感兴趣的:(#,jre)