Java基础之字符串&equals、==、包装类、常量池

在java中有三中对字符串的操作方式。

注:文章只注明思路原理。不注明方法,看API就行了。文章就涉及到啥写啥了,哈哈,瞅着可能乱一点。但是这么写就很舒服

1、String                  常量 效率较低  指的是多进行字符串拼接操作时的效率 final

2、StringBuffer        变量 效率其次  多线程安全

3、StringBuilder      变量 效率最快  多线程不安全

先看看String这个类。这个类被final修饰,所以不能被继承。

对于String方法的改变操作方法都是new了一个新的对象。原对象保存不变。

 先说一下String中的equals。下面是源代码

 

可以看到。976行方法入参是个Object,如果是这样的  str.equals(str);那他执行的时候977行就是this==anObject 直接返回,因为是同一个对象。接下来 980行,判断是不是String及其子类的引用。没有子类,这里判断得就是这个引用是不是String的。然后对比字符串中的char[] 每个索引的元素的相同就返回true。

注:要区分一下哦。引用和对象不是一个东西。引用是存放在栈中的,内存地址固定。而对象是存在于堆中的。

注:instanceOf是判断这个对象是不是右边类的实例。

java存放数据的地方有如下这么几个地方(JVM的东西java基础搞定了在写笔记)

1.寄存器。也就是最快的地方,属于处理器的内部。比内存还快。

2.栈。仅次于寄存器。入栈就是开拓一个新的内存地址。出栈便是释放内存地址。有个类似指针的东西来控制访问、存的是基本数据类型的值   对象的引用。 真正的对象,也就是Object的子类对象存放的位置是堆内存中(以及常量池)。线程私有。

3.堆。存放的是对象。也就是最占地方的。线程共享。

4.常量池。也就是不可变的字符串。这个是线程共享的。

5.程序计数器PC。存放的是下一条指令的地址。遍历的是字节码文件。

6.静态区。在程序初始化时会最先初始化静态变量。有特定位置存放静态数据。而不是堆栈中。

说一下==和equals的区别以及hashcode吧。

一、 ==

1、首先 == 这个操作符在基本数据类型中可以理解成值是否相等,对于 int a = 10;  double b = 10.0  a==b  true

==会将a和b转成相同的数据类型进行比较。

byte boolean  char  short  int float long double       boolean取值为false,true默认是false

1  、 1、           2、     2、 4、 4、  8、    8   byte字节  取值范围就是 2的字节数*8(一个字节=8B)次幂,再看符号位。

对于char无论是中文还是英文一个字符就是占2个字节。

对于整型。+ 操作符会转成int操作。  byte b = (byte) 128 ; 这个会溢出的。取补码,也就是二进制取反+1;  输出-128

int a = 1000;
long b = 1000;
double c = 1000.0;
double d = a;
int e = (int) c;    //高向低转需要强转。

2、对于Object的子类,==表示的是是否为同一个对象。即存放在堆内存中的地址是否相等。

二、equals

首先equals是只有对象才能用的。基本数据类型不能用(不要混淆基本数据类型和包装类),因为这个是Object的方法。

下面是Object最初的equals方法。子类需要重写来定义相等的情况

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

可以看出。原始的equals相当于==

说到equals,把包装类顺便也说了吧。0.0   包装类与基本数据类型最大的区别就是它允许存NULL值

首先对于包装类型。对于数值型都有一个父类就是Number.class。  加上Character  Boolean 构成

public static boolean parseBoolean(String s) {
    return ((s != null) && s.equalsIgnoreCase("true"));
}
public Short(String s) throws NumberFormatException {
    this.value = parseShort(s, 10);
}

有个共同的特征。除了Character的构造是char,其余的构造都能传String,然后在parse转换。

而对于转成基本数据类型。下面是Short的源码、

int a = 10;
Integer aa = Integer.valueOf(a);  //包装类与基本数据类型的转换。 相当于自动装箱。

Integer b = new Integer(10);

int bb = b;    // 相当于Integer.intValue()  自动拆箱。   Integer内部有个int类型的value变量。这个方法把这个value返回了

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}
public int intValue() {
    return value;
}

对于包装类中的equals。

public boolean equals(Object obj) {
    if (obj instanceof Integer) {
       return value == ((Integer)obj).intValue();//value变量是类内部的int型 再把入参强转+拆箱。再用基本数据类型==比较
    }
    return false;
}

所以包装类 == 是比较是否为同一个对象(或者在常量池范围内)。equals就是看值相不相等。而包装类与基本数据类型的比较就是先拆箱再比较。对于包装类的常量池,每个类不一样,统共就那几个。Integer a = 127; Integer b = 127 true; 换成128就是false

Byte--Long -128~127  Character 0~127  Float Double没有。 Boolean true false

而上文的double,int案例就不存在了。因为equals会判断是否是integer类的实例。

个人认为:基本数据类型是存在栈中的,比较快。而包装类为基本数据类型提供了基于对象的操作。

三、hashCode   参考:Java hashCode() 和 equals()的若干问题解答 - 如果天空不死 - 博客园

1.没涉及到Hash存储结构。此时Hashcode没啥实际意义。没涉及到Hash的情况equals等就ok了。

2.涉及到HashMap等存储结构。Hashcode的值相等时还得equals相等才能算作等。光Hashcode值相等还有哈希冲突的情况。

泛型作为key时,必须Hashcode和equals都重写、hashcode要是不等,就相当于存到俩个位置了。Hash相等再判断equals

看下HashMap中node的equals源码吧。

public final boolean equals(Object o) {
    if (o == this)
        return true;
    if (o instanceof Map.Entry) {
        Map.Entry<?,?> e = (Map.Entry<?,?>)o;
        if (Objects.equals(key, e.getKey()) &&     //注意这个是Objects不是Object
            Objects.equals(value, e.getValue()))
            return true;
    }
    return false;
}
public static boolean equals(Object a, Object b) {
    return (a == b) || (a != null && a.equals(b));   //此时比较equals就是泛型重写的equals了。

接着回到String

emmm。这个时候得提一下对象的创建方式

对象有如下这么几个创建方式

1.new 这个很容易,只要构造方法不是private。外部就能随便new(不要抬杠说protected..)

2.反射(下篇文章会介绍) Class.newInstance。或者用Class的构造来创建

3.clone对于实现Cloneable接口的类可以使用这个方法来创建对象。

4.反序列化。在反序列化的时候可以用ObjectInputStream来读取对象。转换成指定的类。

String s = "abc";   String s = new String("abc"); 这是String的创建方式。比较特殊

String s = "abc"; 这个是将s这个引用存放到栈内存中,并在常量池中查找"abc"。查到就返回,没查到就new一个放进常量池。这种如果常量池中存在就不必创建一个新对象了。常量池用来存储运行中创建的字符。String有个intern方法调常量池的引用

String s = new String("abc");这个就是在堆中创建这么一个对象。堆中不要求唯一,创建就开辟一块新地址。

对于这个例子。String s = "abc"; String s1 = "a"+"bc";    s==s1  true   //本处拼接没有用原生引用。是常量池中引用拼接

对于这个例子。String s = "a"; String s1 = "bc"; String s2 = "abc";  s2==(s+s1); 返回的是false;因为s+s1是原生引用拼接。jvm在编译阶段会进行优化,生成一个StringBuffer对象,对每个+连起来的做append操作,最后toString成一个新的对象,放堆里。下面讲一下反编译过程。

下图为StringBuffer的toString()方法。不用管别的行,看673行就知道new了一个新的对象扔堆里了。

tips:堆里面的对象没有被引用,慢慢就被GC回收了。(jvm文章会详细说明)

对于如上这个操作,我们可以用javap反编译来解释。

先放一下源文件代码给大家瞅一眼

在class文件处。shift+鼠标右键打开终端。我把system的部分解释去掉了。

这俩图已经表示的很明显了。

简单总结一下。对于String的equals比较各个位置的字符是否都相等。 == 在没有new或者栈引用拼接时候就相等。

这个是String中重写的Hashcode方法

接下来说一下String中的getBytes方法。因为这个面试时候有被问过。 对于编码就不做过多介绍了。在写代码时候涉及到编码时一定要指明编码格式。否则放到另一个平台就会乱码。windows下GBK ,unix默认是iso

可以看到。一个中文字符GBK占2,utf-8占3,ISO占1

关于String还有最后一个问题。使用final是为什么。

首先final是   private final char value[];  来实现的、引用不可变。

String s = "abc";  s= "def"; 这个是换栈中s位置存放的常量池中的引用。而自身引用是不变的。变得是引用中存放的东西。

String设计成final的为了保证线程安全。并且使用常量池,使操作的效率变高。

上图是对比。对于StringBuffer,不想把之前的值改变。但是事与愿违。

关于String介绍的差不多了。接下来说说StringBuffer和StringBuilder

对于三者的区别。

1.执行速度  StringBuilder>StringBuffer>String 

2.StringBuffer是线程安全的,StringBuilder不是。而String是常量。

多线程用StringBuffer,总操作字符用StringBuffer或者StringBuider。字符操作少用String。(我代码里还没用过StringBuilder。。)

StringBuffer的操作保证线程安全,方法都带了个synchronized

他俩都继承于AbstractStringBuilder 2倍+2扩容。一个线程安全一个不安全,一个快一个稍慢。

void expandCapacity(int minimumCapacity) {    //AbstractStringBuilder 的扩容方法
    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);
}

方法都差不多。看看API就好了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 在Java中,可以使用String类来创建字符串。在判断两个字符串是否相等时,可以使用equals方法和==运算符。 equals方法是用来比较两个字符串的内容是否相等的,它会比较两个字符串的每个字符是否相等,如果相等则返回true,否则返回false。 而==运算符是用来比较两个字符串对象的引用是否相等的,如果两个字符串对象的引用指向同一个对象,则返回true,否则返回false。 因此,在判断两个字符串是否相等时,应该使用equals方法,而不是==运算符。 ### 回答2: Java语言中字符串的处理,是非常重要的一部分内容。String类是Java中的字符串类,它提供了一些比较常用的方法,其中包括equals和==。关于这两种方法,判断字符串是否相等,以下内容做一些介绍和解析。 首先,需要了解的是:Java字符串是不可变的,也就是说一旦定义了一个字符串,它的值就不能被修改,如果要对它进行修改,则需要新创建一个字符串。这种特性,也是这两种字符串相等判断方法的一个原因。 当使用equals方法时,它会比较两个字符串的内容是否相同。例如: ``` String str1 = "hello"; String str2 = "hello"; boolean result = str1.equals(str2); //true ``` 可以看出,这里的result变量的值为true,这是因为这两个变量的值相等。那么,如果现在str2不是使用直接赋值的方式,而是使用new关键字创建,则两者是否相等呢?代码如下: ``` String str1 = "hello"; String str2 = new String("hello"); boolean result = str1.equals(str2); //true ``` 同样可以看到,这两个变量的值也是相等的,也就是equals方法返回的值是true。 接下来,再看一下使用==判断字符串相等的方法。当使用==时,它会比较两个变量的值是否相等,也就是判断两个变量是否指向同一个对象。例如: ``` String str1 = "hello"; String str2 = "hello"; boolean result = (str1 == str2); //true ``` 此时,变量result的值是true,因为这两个变量指向的是同一个对象。但是,如果现在str2变量不再是使用直接赋值的方式,而是使用new关键字创建,如下: ``` String str1 = "hello"; String str2 = new String("hello"); boolean result = (str1 == str2); //false ``` 可以看到,此时result的值为false,因为这两个变量指向的是两个不同的对象。 总结一下,使用equals方法可以判断两个字符串的内容是否相同,即使这两个字符串是通过不同的方式创建的;使用==方法则判断两个变量指向的是否是同一个对象。因此,在使用字符串时,要根据实际情况选择适当的方法来判断字符串是否相等。 ### 回答3: Java中的字符串是不可变的对象,每次对其进行操作都会创建一个新的字符串对象。因此,在比较字符串是否相等时,我们需要使用equals方法,而不是==操作符。 String类提供了equals方法来比较两个字符串对象的内容是否相等。这个方法比较的是字符串的内容,即两个字符串所包含的字符序列是否完全一样。例如, String str1 = "Hello"; String str2 = "world"; boolean result = str1.equals(str2); System.out.println(result); // false 在上面的代码中,str1和str2的内容不一样,因此result的值为false。 相反,==操作符比较的是两个对象的地址是否一样,即它们是否引用同一个对象。例如, String str1 = "Hello"; String str2 = "Hello"; boolean result = str1 == str2; System.out.println(result); // true 在上面的代码中,str1和str2都是"Hello",它们引用的是同一个字符串对象,因此result的值为true。 但需要注意的是,当我们使用new运算符创建字符串对象时,==操作符将无法判断两个字符串对象的内容是否相等。例如, String str1 = new String("Hello"); String str2 = new String("Hello"); boolean result = str1 == str2; System.out.println(result); // false 在上面的代码中,str1和str2都是新创建的字符串对象,它们引用的地址不同,因此result的值为false,而不是我们想要的true。因此在判断两个字符串对象是否相等时,我们应该使用equals方法,而不是==操作符。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值