String类以及Object类

本文深入探讨了Java中java.lang.Object类的各种方法,包括clone、equals、finalize、hashCode和toString等,以及String类的特性和正则表达式应用。同时,讲解了String类的不可变性、+和+=的重载机制,以及如何高效地使用StringBuilder。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

部分内容和代码借鉴自《Java 编程思想》,《深入理解Java虚拟机》 ,《Java™ Platform
Standard Ed. 8》

1.java.lang.Object

Class Object是类Object结构的根。 每个班都有Object作为超类。 所有对象(包括数组)都实现了这个类的方法。

Object类的几种常用方法
  • protected Object clone():创建并返回此对象的副本。

(一) 这里的克隆指的的浅克隆,关于克隆方式可分为以下两种:

  • 深克隆:克隆对象,如下所示,node和node0的成员变量的值完全一致,但却是两个不同的引用。
class TreeNode {
    int val = 1;
    TreeNode left = null;
    TreeNode right = null;
    public TreeNode(int val) {
    this.val = val;
    }
}
public static void main(String[] args){
    TreeNode node0 = new TreeNode(1);
    TreeNode node = new TreeNode(node0.val);
    TreeNode node1 = node0;
    node.left = node0.left;
    node.right = node0.right;
}
  • 浅克隆:克隆引用,如上述的node1,当我们修改node1的val属性时,node0的val也会改变,这是因为浅克隆只是复制引用,并没有复制对象。

(二) 可以注意到clone() 方法的修饰符是protected属性的,所以如果我们需要使用这个方法,我们可以重写 clone() 方法,重写的时候会抛出一个异常:CloneNotSupportedException
这是因为我们并没有实现接口 Cloneable,我们可以查看一下这个接口的源代码:

public interface Cloneable {
}

可以发现接口是空的,其实Java中存在大量这样的接口,他们的作用是标记,即表明这个是可以进行某种行为的,也就是说,在进行我们需要的操作之前,我们可以使用 instanceof 来检验一下类是否具有某种标志。

  • boolean equals(Object obj):指示一些其他对象是否等于此。

以下的 equals() 方法的源码,可见,在不进行重写的情况下, equals()== 完全等价。

public boolean equals(Object obj) {
      return (this == obj);
}

但是,在大多数情况下,我们会根据实际情况对 equals() 进行重写以满足我们的使用需求,下面是一种经典的重写方法:

public class Demo1{
   public static final int VALUE = 3;
   public String name;
   public Demo1(String name) {
       this.name = name;
   }
   @Override
   public boolean equals(Object obj) {
       if (obj == null)  // 判断是否为null
           return false;
       if (obj instanceof Demo1) {
           Demo1 b = (Demo1)obj;   
           return b.name == this.name;
       }
       return false;
   }
}
  • protected void finalize():当垃圾收集确定不再有对该对象的引用时,垃圾收集器在对象上调用该对象。

该函数用来对垃圾进行回收,值得注意的是,每个对象的 finalize()只会被系统调用一次。Java的垃圾回收不需要程序员亲自实现,它会自动收集垃圾,当然我们也可以手动调用System.gc() 来进行垃圾回收。函数对于一个对象是否属于垃圾,目前有很多种经典的判断方法,其中效率较高比较简单的是引用计数算法,但是该方法存在缺陷,它很难解决对象之间互相循环引用的问题。因此,在主流的商用语言中,都是使用可达性分析计算,这里不多做介绍。

  • int hashCode():

哈希算法可以将任意长度的二进制值引用为较短的且固定长度的二进制值,把这个小的二进制值称为哈希值,这里不对哈希算法进行深入的探讨,我们可以简单的理解为哈希码是对对象内存地址的一种表达。我们要讨论的该方法的重写,在Java中 == 判断的依据之一就是调用hashCode() 方法时返回的哈希值是否相等,我们在之前已经重写了equals方法因此很有可能出现被 equals 判断为相等两个对象哈希值并不相同,所以我们需要重写。

@Override
   public int hashCode() {
       int result = name != null ? name.hashCode() : 0;
       result = result * 31 + this.age != 0 ? 1 : 0;
       return result ;
   }

在重写的时候我们可以使用一个质数 * 类内引用对象的哈希值再加上其他成员的一些特性来完成哈希值,我们在重写时,一定要遵守由 equals 方法判定为相同的对象的哈希值一定要相同,而被判定为不同的对象的哈希值尽量不相同

  • Class<?> getClass():

在我的另一篇博客中有相应的介绍RTTI,Class类以及类加载过程【学习心得】

  • String toString():

返回对象的字符串表示形式,源码如下,可以根据自己需求重写:

public String toString() {
      return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
  • 余下的一些方法都是线程相关的方法,这里不做介绍。

2.String类

String对象是不可变的,查看JDK文档你就会发现,String类中每一个看似对String进行修改的方法,其实都是创建了一个新的String对象,而最初的String对象都没有被修改。

++= 重载

在Java中,只有 ++= 两种操作符是重载的,值得注意的是,Java并不允许程序员像C++一样重载运算符,当我们使用 + 来连接两个字符串时:

System.out.println("aaa" + "bbbb");

底层实际上进行的操作是创建了一个StringBuilder对象来进行append操作,

for (int i = 0;i < n;i++) {
		System.out.println("aaa" + "bbbb");
}

而对于上述在循环中进行字符串连接操作,则会在每次循环时创建一个StringBuilder对象,可见效率之低。因此我们在重写自己的toString() 方法时,应该尽量使用StringBuilder的append操作,尽量避免使用 +:
Tips: 效率 StringBuilder > StringBuffer > String (但是StringBuilder不是线程安全的)

String类的方法

String类拥有许许多多的方法,在这里只介绍之中,余下的大家可以自行去查看JDK文档

  • byte[] getBytes():

使用平台的默认字符集将此 String编码为字节序列,将结果存储到新的字节数组中,即进行IDE默认的编码方式进行解码:

public static void main(String[] args) throws Exception {
     String a = "学习";
     byte[] bsGBK = a.getBytes("GBK");
     String strGBK = new String(bsGBK, "GBK");
     String strUTF8 = new String(bsGBK, "UTF-8");
     System.out.println(Arrays.toString(bsGBK)); // 解码
     System.out.println(strGBK); // 编码
     System.out.println(strUTF8); // 乱码
}
result:
[-47, -89, -49, -80]
学习
ѧϰ

可见我们常见的乱码问题就是使用了错误的编码方式进行了编码,值得注意的是,因为大多数的编码方式对于英文字母的编码都是一致的,所以我们经常出现注释乱码但是代码依然没有乱码的情况。
在这里插入图片描述

正则表达式

正则表达式是一种灵活强大的文本处理工具,使用正则表达式,我们能够以编程的方式构造复杂的文本模式,并对输入的字符串进行搜索。正则表达式提供了一种通用的方式解决字符串相关的问题:
匹配、选择、验证以及编辑问题。

符号含义
表示一个或0个符号
+表示一个或多个符号
\\\\插入一个反斜杠符号
.任意字符
\n换行符
\t制表符
\r回车符
[abc]表示a或b或c
[^abc]表示出了abc以外的字符
[a-zA-Z]表示a-z和A-Z的英文字母
[a-z && [hig]]表示h或i或g,相交
\d表示一个0-9的数字
s
\S表示非空白符
\w表示词字符[a-zA-Z0-9]
\W表示非词字符
^表示一行的开始
$一行的结束

还有许多,,,这里不一一列出了。

String内建的正则表达式工具
  • split():可以根据给定的正则表达式对字符串进行分割,分割成String[]。
  • matches():根据给定的正则表达式对字符串进行匹配,如果匹配则返回 true,否则返回false。
  • replace()/replaceAll():根据给定的正则表达式对字符串进行匹配,对匹配的部分进行替换。
CharSequence接口

CharSequence接口从String、StringBuilder、CharBuffer、StringBuffer中抽象出了字符序列的定义:

interface CharSequence {
	charAt(int i);
	length();
	subSequence(int start, int end);
	toString();
}
Pattern和Matcher

虽然String提供了一些正则表达式的工具,但是任然无法满足我们的需求。在java.util.regex中,我们可以使用Pattern和Mathcer来构建更加强大的正则表达式。

		String regex = "\\d+(\\.\\d+)?";  // 无符号小数或者整数的正则表达式
        String str = "1.33+2";
        String[] num = new String[2];
        Matcher match = Pattern.compile(regex).matcher(str); // 使用Pattern.complile()编译  
        int i = 0;											 // 正则表达式。
        while (match.find()) {
            num[i++] = match.group();
        }

上面的code片段实现了捕捉一个二元运算表达式的两个运算对象的功能。其中我们使用到了Matcher.find() ,Matcher.group()方法。

  • Matcher.find():相当于一个迭代器,每次从上次匹配完成的地方开始进行匹配
  • Matcher.group():返回上次find()匹配到的字符串,如果没有匹配到,则返回null
  • Matcher.start():返回上次匹配到的字符串的开始位置,若没有匹配到则会返回 IllegalStateException
  • Matcher.end():返回上次匹配到的字符串的结束位置,若没有匹配到则会返回 IllegalStateException
lookingAt() , matches(), find()的区别
  • lookingAt()只有在正则表达式与输入的字符串从开始就匹配成功才会返回true,否则返回false
  • matches()只有在正则表达式和整个输入字符串匹配成功的情况下才会返回true
  • find()可以在输入字符串的任意位置开始匹配字符串
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值