JAVA常用API:java.lang.Object、java.lang.String

JAVA常用API:java.lang.Object、java.lang.StringJava 中常用 API java.lang.Object、java.lang.String 的讲解。

这是我参与8月更文挑战的第23天,活动详情查看:8月更文挑战

文章目录

API

API,英文全称 Application Programming Interface,翻译为“应用程序编程接口”。是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。

Java 的核心 API 是非常庞大的,这给开发者来说带来了很大的方便。所谓的 API 就是一些已经写好、可直接调用的类库。Java 里有非常庞大的 API,其中有一些类库是我们必须得掌握的,只有熟练掌握了 Java 一些核心的 API,我们才能更好的使用 Java。

我们目前学习阶段常用的 API 如下:

java.lang.Object
lang – language 语言包。在程序中,java.lang 包并不需要像其他包一样需要 import 关键字进行引入。系统会自动加载,所以我们可以直接取用其中的所有类。
java.lang.String
java.lang.StringBuilder
java.lang.StringBuffer
正则表达式
java.util.regex.Pattern
java.util.regex.Matcher
基本类型包装类
java.math.BigDecimal
java.math.BigInteger
java.text.DecimalFormat
java.lang.Math
日期相关类型
java.util.Date
java.text.SimpleDateFormat
java.util.Calendar
java.util.GregorianCalendar

下面这章主要介绍 java.lang.Object 和 java.lang.String

java.lang.Object

Object 类 是 java 中所有类的顶层父类,一个类如果不继承其他类,默认继承Object,省略了 extends Object。

class A extends Object{
}
我们只需写成
class A {
}

方法

1、toString()

Object 类的 toString 方法返回一个字符串,该字符串由类名、标记符“@”和此对象哈希码的无符号十六进制表示组成。

例如我们有一个 Point 类,包含坐标点 x 和 y

public class Point {
    int x;
    int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

我们创建 Point 实例,然后用 Log 输出 toString() 方法的结果:

Point point = new Point(3, 4);
Log.d("TTT", point.toString());
//输出结果
D/TTT: com.example.testapplication.Point@640983e

格式为:包名@内存地址。这种不是我们需要的,所以这个方法一般需要重写

public class Point {
    ......(内容同上)

    public String toString() {
        return "(" + x + "," + y + ")";
    }
}

再次运行程序,查看打印结果

D/TTT: (3,4)

2、equals(Object obj)
当前对象与参数对象 obj 是否是同一对象,也就是比较内存地址是否相等。

Point point1 = new Point(3, 4);
Point point2 = new Point(3, 4);
Log.d("TTT", "" + point1.equals(point2));

输出结果是 false,因为内存地址不同。如果需要,可以在子类中重写这个方法,比较对象的属性是否相等。

public class Point {
    ......(内容同上)

    @Override
    public boolean equals(Object obj) {
        if (obj == null) return false;
        if (obj == this) return true;
        if (!(obj instanceof Point)) return false;
        Point p = (Point) obj;
        return
                this.x == p.x && this.y == p.y;
    }
}
Point a = new Point(3, 4);
Point b = new Point(3, 4);

Log.d("TTT", "" + (a == b));//输出 false
Log.d("TTT", "" + (a.equals(b)));//输出 true

3、hashCode()
Object 的 hashcode() 方法用于哈希查找,可以减少在查找中使用 equals 的次数,重写了 equals 方法一般都要重写 hashCode 方法。

hashcode() 方法返回一个整形数值,表示该对象的哈希码值,默认使用内存地址值作为哈希值

如果需要,可以在子类中重写这个方法,使用属性数据来计算产生哈希值。

Point point = new Point(3, 4);
Log.d("TTT", "" + point.hashCode());//输出 104896574

java.lang.String

介绍
String 类也是java.lang 包下的一个类。一个 String 字符串实际上是一个 final 修饰的 char 数组。

有兴趣的同学可以查看 String 源码,可以发现:
1、String 是一个用 final 声明的常量类,不能被任何类所继承。
2、String 类的主力成员字段 value 是个 final 修饰的 char[] 数组。final 修饰的字段创建以后就不可改变。该类中的一些方法看似改变了字符串,其实内部都是创建一个新的字符串。也就是这些操作都不是在原有的字符串上进行的,而是重新生成了一个新的字符串对象,这些操作后,最原始的字符串并没有被改变。

字符串的创建

1、第一种方法,通过“字面量”的形式直接赋值

String a = "hello";

2、第二种方法,通过 new 关键字调用构造函数创建对象

char[] a = {'h','e','l','l','o'};
String s = new String(a);

二者的区别我们可以通过一个例子来理解一下:

String a = "hello";
String b = new String(new char[]{'h', 'e', 'l', 'l', 'o'});
String c = "hello";
String d = new String(new char[]{'h', 'e', 'l', 'l', 'o'});
Log.d("TTT", "" + (a == b));//false
Log.d("TTT", "" + (a == c));//ture
Log.d("TTT", "" + (b == d));//false

第一种方法,通过“字面量”的形式直接赋值时,会在“字符串常量池”(下边会解释)中新建实例,再次使用相同字面值时,直接访问常量池中存在的对象,而不会新建对象。也就是 a 在字符串常量池创建 “hello”,c 在创建的时候检查字符串常量池中有 “hello” 对象,直接返回。

通过 new 关键字来生成对象是在堆区进行的,而在堆区进行对象生成的过程是不会去检测该对象是否已经存在的。因此通过 new 来创建对象,创建出的一定是不同的对象,即使字符串的内容是相同的。

另外,String 是引用类型,用 == 判断是否是同一个对象。使用符号进行的运算都在栈内存中完成,比如===等。因此比较的是引用地址或内存地址是否相等。

不可变

String 在内存中本质是数组存储的数据,数组长度不可变,创建出来的字符串对象不可变,但是变量可变。例如 String a = "hello"; s 这个变量的值是可以改变的,但 “hello”这个字符串是不可变的。

因为字符串有不可变性,因此反复创建字符串是件很消耗内存的事,因此在内存中专门指定了一片区域来存储各种常量,而在常量这个区域专门有一个区域来存放字符串常量,叫做字符串常量池,它有一个特性就是,字符串是唯一的。

例如:

String s1 = "hello";
String s2 = "hello";
Log.d("TTT", "" + (s1 == s2));//true

创建了 s1,字符串常量池有1个 “hello”,当再创建 s2 时,因为也是 “hello”,因此常量池中就不再创建了,s2 直接指向之前创建的常量。

而用关键字 new 创建就不同了。

String s3 = new String("hello");
String s4 = new String("hello");
Log.d("TTT", "" + (s3 == s4));//false

只要看见 new 创建的对象就一定在堆内存,new 表示申请一片新的空间去存储。

对此我们可以拓展。

String s5 = "he" + "llo";
Log.d("TTT", "" + (s1 == s5));//true

s5 是由 2 个常量相加得到的,常量+常量=常量,这个过程在编译过程中就直接完成了,不需要等程序运行,所以在程序运行时,s5 就等于“hello”,所以 s1 == s5 返回 true

String s6 = "he";
s6 += "llo";
Log.d("TTT", "" + (s1 == s6));//false

s6 的值是 变量+常量=变量,对于程序而言,变量只有在程序运行时才知道,而这时候得到的值绝对不可能在常量池里,所以,s6 产生的值在堆内存里,而 s1 在常量池里,所以 s1 == s6 返回 false。

String s7 = s1;
Log.d("TTT", "" + (s1 == s7));

s7 是的赋值。是直接把 s1 的引用地址赋值。因此 s1 == s7 返回 true

StringBuilder 和 StringBuffer 存在的必要性

上一篇文章中,我们讲了 StringBuilder 和 StringBuffer。既然在 Java 中已经存在了 String 类,那为什么还需要 StringBuilder 和 StringBuffer 类呢?原因就是因为字符串一旦创建出来,就不可变,直接表现就是每次连接都创建新实例,这导致字符串连接效率低。

上一章我们使用 StringBuilder 对字母 a-z 拼接了 100000 次 ,只用了 22 毫秒。下面我们测试下用 + 拼接使用的时间。

String s0 = "abcdefghigklmnopqrstuvwxyz";
long t = System.currentTimeMillis();
String b = "";
for (int i = 0; i < 1000; i++) {
	b += s0;
}
System.out.println(System.currentTimeMillis() - t);

执行结果用了 73 毫秒。明显比 StringBuilder 慢了很多。
在这里插入图片描述
StringBuffer 的效率要远远高于String,String 的+= 运算,每次都要创建新的字符串,每一次都要新申请内存空间。而 StringBuffer 是用空间换取时间的做法,开始就准备一块很大的空间,每次拼接,直接在字符串后面拼接即可。

在高频率的字符串拼接时,5次以上,应该用 StringBuffer 和 StringBuilder,不应该用String。

上一章我们讲到,StringBuffer 是安全的,StringBuilder 是不安全的。所谓的安全和不安全表现为,多个线程处理同一个数据的时候安全不安全。

例如用 StringBuilder = “abc”,有 3 个线程都要改变它的值,3 个线程可能运行在同一时间点,那么这个值就不可控了。而 StringBuffer 就可以保证这一点。其实在不考虑多线程时,使用StringBuilder就可以了。

关于 String 的其他文章可以查看我写的文章:
字符串相关方法StringBuilder 和 StringBuffer

今天的文章JAVA常用API:java.lang.Object、java.lang.String分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/15511.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注