常用类详解(一)包装类和String类详解

文章详细介绍了Java中的包装类,如Integer和Character的特性,包括它们的常量池行为、自动装箱和拆箱、常用方法。同时,文章深入讲解了String类的不可变性、创建对象的方式、字符串拼接的原理以及常用方法如equals、compareTo等。此外,还涉及到了比较器Comparable的概念和使用。

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

一、包装类 

包装类的分类 (Wrapper)

  1. 针对八种基本数据类型相应的引用类型--包装类
  2. 有了类的特点,就可以调用类中的方法。

基本数据类型

包装类

boolean

Boolean

char

Character

double

Double

int

Integer

byte

Byte

short

Short

float

Float

long

Long

后六个的父类都为Number。

(1)我们查看Boolean的源码:

我们进入类图如下所示:

Boolean实现了Serializable和Comparable接口。

(2)我们查看Character的源码:

我们查看他的类图:

Character类实现了Serializable和Comparable。

(3)我们查看Byte类:

我们进行查看他的类图:

我们查看剩下的类图:

Float\Double\Intger\Byte\Short\Long 都继承自Number类,实现了Comparable,同时也实现了Serializable接口。

包装类和基本数据的转换

演示 包装类和基本数据类型的相互转换,这里以int和Integer演示。

(1)jdk5前的手动装箱和拆箱方式,装箱:基本类型 ->包装类型,反之,拆箱:包装类型 -->基本类型

(2)jdk5 以后(含jdk5)的自定装箱和拆箱方式

(3)自动装箱底层调用的是valueOf方法,比如Integer.valueOf( )

(4 )其它包装类的用法类似。

我们查看示例如下所示:

package com.ypl.wrapper;

public class Integer01 {
    public static void main(String[] args) {
        //演示int <-->Integer的装箱和拆箱
        //装箱:  int  -->Integer
        //拆箱:  Integer -->int
        //jdk5前是手动装箱和拆箱
        //手动装箱   int ->Integer
        int n1=100;
        //方式1:
        Integer integer = new Integer(n1);
        //方式2:
        Integer integer1 = Integer.valueOf(n1);

        //手动拆箱  Integer -->int
        int i = integer.intValue();

        //jdk5后,就可以自动装箱和自动拆箱
        int n2=200;
        //自动装箱  int ->Integer
        Integer integer2=n2;  //底层使用的是Integer.valueOf(n2)。
        //我们进行debug,我们进入底层发现源码如下所示:
        /**
         * public static Integer valueOf(int i) {
         *         if (i >= IntegerCache.low && i <= IntegerCache.high)
         *             return IntegerCache.cache[i + (-IntegerCache.low)];
         *         return new Integer(i);
         *     }
         */
        //自动拆箱 Integer -->int
        int n3=integer2;   //底层仍然使用的是intValue()方法
        //我们继续进行debug,我们进入底层发现源码如下所示:
        /**
         * public int intValue() {
         *         return value;
         *     }
         */



    }
}

课堂测试题:

下面的代码是否正确,为什么?

Double d=100d; //ok,自动装箱 Double.valueOf(100d);
Float  f=1.5f;  //ok,自动装箱 Float.valueOf(1.5f);

如下两个题目输出结果相同吗?各是什么?

Object obj1=true?new Integer(1):new Double(2.0);  //三元运算符【是一个整体】  如果为真,则返回第一个值,如果为假,则返回第二个值。
System.out.println(obj1);//什么?  输出1.0,Double会提升优先级。
Object obj2;
if(true)
obj2=new Integer(1);
else
obj2=new Double(2.0);
System.out.println(obj2);

输出什么?  1,分别计算,是独立的,不会提示优先级

我们在编译软件上实际编码如下:

package com.ypl.wrapper;

public class WrapperExercise01 {
    public static void main(String[] args) {
        Double d=100d; //ok,自动装箱 Double.valueOf(100d);
        Float  f=1.5f;  //ok,自动装箱 Float.valueOf(1.5f);

        Object obj1=true?new Integer(1):new Double(2.0);  //三元运算符【是一个整体】  如果为真,则返回第一个值,如果为假,则返回第二个值。
        System.out.println(obj1);//什么?  输出1.0,Double会提升优先级。

        Object obj2;
        if(true)
            obj2=new Integer(1);
        else
            obj2=new Double(2.0);
        System.out.println(obj2);
    }
}

运行之后如下所示:

包装类型和String类型的相互转换

我们示例代码如下所示:

package com.ypl.wrapper;

public class WrapperVSString {
    public static void main(String[] args) {
        //案例演示,以Integer和String转换为例,其他类似:
        //包装类(Integer) -->String
        //1.自动装箱
        //2.toString方法
        //3.valueOf方法
        Integer i= 100; //自动装箱
        //方式1,i本身没有变化
        String str1=i+"";
        //方式2
        String str2 = i.toString();
        //方式3
        String str3 = String.valueOf(i);
        /*
        public static String valueOf(Object obj) {
        return (obj == null) ? "null" : obj.toString();
    }
    valueOf底层为Object对象,而Integer本身就是从Object过来的。
         */

        //String  -->包装类(Integer)
        //1.Integer的parseInt方法
        //2.Integer的构造器的形式
        String str4="123450";
        Integer i2 = Integer.parseInt(str4);  //使用到自动装箱
        /*
        我们查看parseInt的底层代码:
         public static int parseInt(String s) throws NumberFormatException {
        return parseInt(s,10);
    }
         */

        Integer i3 = new Integer(str4); //构造器
        /*
        我们查看构造器的源码如下所示:
         public Integer(String s) throws NumberFormatException {
        this.value = parseInt(s, 10);
    }
         */
        System.out.println("ok----");


    }
}

我们运行之后如下所示:

我们发现没有问题。

char [ ] 和String类型的相互转换

package com.ypl.String;

/**
 * String与 char[]之间的转换
 * String -->char[]:调用String的toCharArray()
 * char --->String:调用String的构造器
 */
public class StringChar {
    public static void main(String[] args) {
        String str1="abc123";     //题目:a21cb3,可以将字符串变成数组,然后利用数组的方法进行反转
        char[] charArray = str1.toCharArray();
        for(int i=0;i<charArray.length;i++){
            System.out.println(charArray[i]);
        }
        char[] arr=new char[]{'h','e','l','l','o'};
        String s = new String(arr);
        System.out.println(s);
    }

}

运行之后如下所示:

字节数组和String类型的相互转换

package com.ypl.String;

import java.io.UnsupportedEncodingException;
import java.util.Arrays;

/**
 * String与byte[ ]之间的转换
 * 编码:String -->byte[]:调用String的getBytes()
 * 解码:byte[] -->String:调用String的构造器
 *
 *
 *
 * 编码:字符串-->字节(看得懂--->看不懂的二进制数据)
 * 解码:编码的逆过程,字节--->字符串(看不懂的二进制数据-->看得懂)
 *说明:解码时,要求解码使用的字符集必须与编码时使用的字符集一致,否则会出现乱码
 */
public class StringByte {
    public static void main(String[] args) {
        String str1="abc123中国";
        byte[] bytes = str1.getBytes(); //使用默认的字符集,进行编码
        System.out.println(Arrays.toString(bytes));


        byte[] gbks = new byte[0];
        try {
            gbks = str1.getBytes("gbk");//使用gbk字符集进行编码
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        System.out.println(gbks);


        String s = new String(bytes);  //使用默认的字符集,进行解码
        System.out.println(s);

        String s1 = new String(gbks);
        System.out.println(s1);//出现乱码。原因:编码集和解码集不一致

        try {
            String gbk = new String(gbks, "gbk");
            System.out.println(gbk);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

}

运行之后如下所示:

包装类(Integer类和Character类)的常用方法

我们发现Integer有很多种方法:

我们选择常见的方法如下所示:

Integer.MIN_VALUE(返回最小值)

Integer.MAX_VALUE(返回最大值)

我们发现Character的方法如下所示:

Character.isDigit(判断是不是数字)

Character.isLetter(判断是不是字母)

Character.isUpperCase(判断是不是大写)

Character.isLowerCase(判断是不是小写)

Character.isWhitespace(判断是不是空格)

Character.toUpperCase(转成大写)

Character.toLowerCase(转成小写)

我们的代码示例如下所示:

package com.ypl.wrapper;

public class WrapperMethod {
    public static void main(String[] args) {

        System.out.println(Integer.MIN_VALUE);//返回最小值
        System.out.println(Integer.MAX_VALUE);//返回最大值

        System.out.println(Character.isDigit('a')); //判断是不是数字
        System.out.println(Character.isLetter('a'));//判断是不是字母
        System.out.println(Character.isUpperCase('a'));//判断是不是大写
        System.out.println(Character.isLowerCase('a'));//判断是不是小写

        System.out.println(Character.isWhitespace('a'));//判断是不是空格
        System.out.println(Character.toUpperCase('a'));//转成大写
        System.out.println(Character.toLowerCase('A'));//转成小写
    }
}

我们运行之后如下所示:

Integer类面试题1:

我们的代码如下所示:

package com.ypl.wrapper;

public class method1 {
    public static void main(String[] args) {
        Integer i = new Integer(1);
        Integer j = new Integer(1);
        System.out.println(i==j);  //False,不是同一个对象
        
        
        
       //所以,这里主要是看范围 -128 ~127就是直接返回
        Integer m=1;  //底层 Integer.valueOf(1);  ->阅读源码
        Integer n=1;  //底层 Integer.valueOf(1);  ->阅读源码
        /**
         * 1.如果i在IntegerCache.low(-128)~IntegerCache.high(127),包括-128和127,就直接从数组返回
         * 2.如果不在-128~127,就直接new Integer(i)
         * public static Integer valueOf(int i) {
         *         if (i >= IntegerCache.low && i <= IntegerCache.high)
         *             return IntegerCache.cache[i + (-IntegerCache.low)];
         *         return new Integer(i);
         *     }
         *     我们继续追加的时候,我们发现
         *      private static class IntegerCache {
         *         static final int low = -128;
         *         static final int high;
         *         static final Integer cache[];
         *
         * This method will always cache values in the range -128 to 127, inclusive,
         * and may cache other values outside of this range.
         */
        System.out.println(m==n);  //返回True.
        
        
        
        
        //所以,这里主要是看范围 -128 ~127就是直接返回
        //否则,就new Integer(xx);
        Integer x=128;//底层 Integer.valueOf(128);
        Integer y=128;//底层 Integer.valueOf(128);

        /**
         * public static Integer valueOf(int i) {
         *         if (i >= IntegerCache.low && i <= IntegerCache.high)
         *             return IntegerCache.cache[i + (-IntegerCache.low)];
         *         return new Integer(i);
         *     }
         */
        System.out.println(x==y); //128不在范围内,因此就返回false。


        Integer a=127;//底层 Integer.valueOf(127);
        Integer b=127;//底层 Integer.valueOf(127);
        //所以,这里主要是看范围 -128 ~127就是直接返回,包括-128和127
        /**
         * public static Integer valueOf(int i) {
         *         if (i >= IntegerCache.low && i <= IntegerCache.high)
         *             return IntegerCache.cache[i + (-IntegerCache.low)];
         *         return new Integer(i);
         *     }
         */
        System.out.println(a==b); //127在范围内,因此就返回true。
    }

}

我们运行之后如下所示:

我们可以更加详细的查看我们的源码,我们进行debug,如下所示:

 我们追加之后,我们发现如下所示:

我们发现 IntegerCache.cache里面东西已经创建好了,而且最低为-128,最高为127。

下来我们继续看待一道Integer类面试题:

我们的代码如下所示:

package com.ypl.wrapper;

public class WrapperExercise03 {
    public static void main(String[] args) {
        //示例一
        Integer i1 = new Integer(127);
        Integer i2 = new Integer(127);
        System.out.println(i1==i2);  //返回false

       //示例二
        Integer i3 = new Integer(128);
        Integer i4 = new Integer(128);
        System.out.println(i3==i4);  //返回false

        //示例三,取值范围在-128~127,直接返回
        Integer i5=127; //底层Integer.valuesOf(127)
        Integer i6=127; //底层Integer.valueOf(127)
        System.out.println(i5==i6);   //返回true

        //示例四,超出直接返回的取值范围 ~128-127。
        Integer i7=128;
        Integer i8=128;
        System.out.println(i7==i8);  //返回false

        //示例五
        Integer i9=127;  //底层Integer.valuesOf(127)
        Integer i10 = new Integer(127);
        /**
         * public static Integer valueOf(int i) {
         *      if (i >= IntegerCache.low && i <= IntegerCache.high)
         *           return IntegerCache.cache[i + (-IntegerCache.low)];
         *         return new Integer(i);
         *   }
         */
        System.out.println(i9==i10); //返回false。

        //示例六,只要有基本数据类型,判断的是值是否相等。
        Integer i11=127;
        int i12=127;
        System.out.println(i11==i12); //返回true,会自动装箱


        //示例七,只要有基本数据类型,判断的是值是否相等。
        Integer i13=128;
        int i14=128;
        System.out.println(i13==i14); //返回true.
    }
}

 我们运行之后如下所示:



二、String类

String类的理解和创建对象

(1)String对象用于保存字符串,也就是一组字符序列,String是一个final类,代表不可变的字符序列,是不能被继承的类。

(2)字符串常量对象是用双引号括起的字符序列。例如:“你好”、“12.97”、“boy"等

(3)字符串的字符使用Unicode字符编码,一个字符(不区分字母还是汉字)占两个字节

         char占两个字节

(4)String类较常用构造器(其他看手册):

//this.value=new char[0];

String s1=new String( );

//this.value=original.value;

String s2=new String(String original);

//this.value=Arrays.copyof(value,value.length);

String s3=new String(char[ ]  a);

String s4=new String(char[ ] a,int startIndex(从哪里开始),int count(多少个字符))

(5)字符串的不可变性:

package com.ypl.String;

/**
 * String的使用
 */
public class StringTest {
    /*
    String:字符串,使用一对" "引起来表示。
    1.String声明为final的,不可被继承
    2.String实现了Serializable接口:表示字符串是支持序列化的。
            实现了Comparable<String>接口:表示String可以比较大小
     3.String内部定义了final char[] value用于存储字符串数据
     4.String:代表不可变的字符序列。简称:不可变性。
     体现:(1)当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值。
           (2)当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能在原有的value进行赋值
           (3)当调用String的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值。
    我们在通过String进行字面量赋值的时候,如果方法区里面的常量池没有这个数,则进行创建,然后地址值赋值给栈,如果要重新赋值,则
    查看方法区里面的常量池是否存在,如果没有,即继续创建,同时新创建的变量的地址指向栈空间,原来的被取消,如果长时间未得到栈的调用,
    被垃圾回收机制回收
     5.通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中。
     6.字符串常量池中是不会存储相同内容的字符串的。
     */
    public static void main(String[] args) {
        String s1="abc"; //字面量的定义方式
        String s2="abc"; //保存在常量池里面的,以地址进行存储
      s1="hello";
        System.out.println(s1==s2);//两个对象用==比较的是地址值
        System.out.println(s1);//hello
        System.out.println(s2);//abc
        System.out.println("******************");
        String s3="abc";
        s3+="def";
        System.out.println(s3);//abcdef
        System.out.println(s2);
        System.out.println("******************");
        String s4="abc";
        String s5 = s4.replace('a', 'm');
        System.out.println(s4);
        System.out.println(s5);
    }

}

 运行之后如下所示:

我们设计如下代码: 

public class String01 {
    public static void main(String[] args) {
        //String对象用于保存字符串,也就是一组字符序列
        String name="jack";
    }
}

我们进入String的底层源码:

我们查看他的类图:

 

 String实现了Serializable这个接口,说明String可以串行化,String对象是可以在网络上传输的。

String实现了Comparable这个接口,说明String对象可以相互比较大小。

String实现了CharSequence这个接口,实现字符序列。

我们查看String的构造器:


 我们的设计如下所示:

package com.ypl.String;

public class String01 {
    public static void main(String[] args) {
        //1.String对象用于保存字符串,也就是一组字符序列
        //2."jack"字符串常量,双引号括起的字符序列
        //3.字符串的字符使用Unicode字符编码,一个字符(不区分字母还是汉字)占两个字节,char占两个字节
        //4.String类有很多构造器,构造器的重载
        //常用的有
        //String s1=new String( );
        //String s2=new String(String original);
        //String s3=new String(char[] a);
        //String s4=new String(char[] a,int startIndex,int count)
        //String s5=new String(byte[] b)
        //5.String类实现了接口Serializable(String可以串行化:可以在网络传输)
        //6.String类实现了接口Comparable(String对象可以比较大小,利用compareTo方法。
        //7.String类是final类,不能被其他的类继承
        /**
         *  8.String有属性 private final char value[];用于存放字符串内容
         *          我们的字符串常量真正存储在String源码里面的:
         *         private final char value[];
         *         在底层仍然是一个char数组。字符串本质仍然是一个char数组。
         */
        //9.一定要注意:value是一个final类型,不可以修改(不是字符不可以修改,而是里面的地址不可以修改,即指向存储的地址不可以修改,
        // 而所指向的地址存储的值是可以修改的):即value不能指向新的地址,但是单个字符内容是可以变化的。
       final char value[] ={'a','b','c'};
       char[] v2={'t','o','m'};
       value[0]='H';
       value=v2;


        String name="jack";
    }
}

为了更清晰的了解:

一定要注意:value是一个final类型,不可以修改(不是字符不可以修改,而是里面的地址不可以修改,即指向存储的地址不可以修改, // 而所指向的地址存储的值是可以修改的):即value不能指向新的地址,但是单个字符内容是可以变化的。

我们设计代码如下:

 final char value[] ={'a','b','c'};
       char[] v2={'t','o','m'};
       value[0]='H';
       value=v2;
//value=v2;不可以修改value地址

这样子是有错误的:

不能给value再分配一个新的变量 

当我们修改代码如下所示时:

char value[] ={'a','b','c'};
       char[] v2={'t','o','m'};
       value[0]='H';
       value=v2;

 我们如下所示:

 我们拿掉final发现是可以的了,final是为了约束不能指向一个新的空间。单独的是可以允许的。

创建String对象的两种方式:

(1)方式一:直接赋值String s="rgflfbc"

先从常量池查看是否有"rgflfbc"数据空间,如果有,直接指向;如果没有则重新创建,然后指向。s最终指向的是常量池的空间地址。

jvm的内存:String的内存布局

  (2)  方式二:调用构造器 String s=new String("rgflfbc");

先在堆中创建空间,里面维护了value属性,指向常量池的rgflfbc空间。如果常量池没有"rgflfbc",重新创建,如果有,直接通过value指向。最终指向的是堆中的空间地址。

 我们进行如下测试:

测试题1:

我们的代码如下所示:

package com.ypl.String;

public class StringExercise01 {
    public static void main(String[] args) {
        String a="abc";
        String b="abc";
        System.out.println(a.equals(b)); //true
        System.out.println(a==b);  //true
    }
}

 我们运行之后如下所示:

 测试题2:

package com.ypl.String;

public class StringExercise01 {
    public static void main(String[] args) {

        String a1 =new String("abc");
        String a2 =new String("abc") ;
        System.out.println(a1.equals(a2)); //true
        System.out.println(a1==a2);  //false
    }
}

运行之后如下所示:

测试题3:

我们的代码设计如下所示:

package com.ypl.String;

public class StringExercise02 {
    public static void main(String[] args) {
        String a="rgf";  //a指向常量池的"rgf"
        String b=new String("rgf"); //b指向堆中的对象 value
        System.out.println(a.equals(b)); //true
        System.out.println(a==b); //false,a指向常量池,b指向堆中的对象 value。
        System.out.println(a==b.intern());//查看intern方法,在API里面查看,返回true,在常量池里面查看是否有b对象的字符串,如果有则返回。
        System.out.println(b==b.intern());b指向堆,即堆中的对象value,b.intern返回常量池的地址。
    }
}

b.intern( )方法最终返回的是常量池的地址(对象) 

我们运行之后如下所示:

我们通过菜鸟教程找到了中文版的API,intern方法的概述如下所示:‘

 调用intern方法时,如果常量池已包含等于此String方法确定的String对象的字符串(用equals(Object)方法确定),则返回常量池中的字符串。 否则,将此String对象添加到池中,并返回对此String对象的引用。

测试题4:

我们设计的代码如下所示:

package com.ypl.String;

public class StringExercise03 {
    public static void main(String[] args) {
        String s1="rgfedu";//指向常量池"rgfedu"
        String s2="java";//指向常量池"java"
        String s4="java";//指向常量池"java"
        String s3=new String("java");//指向堆中对象
        System.out.println(s2==s3);//false,s2指向常量池,s3指向堆
        System.out.println(s2==s4);//true,都指向常量池
        System.out.println(s2.equals(s3));//true,常量池里面的值相等
        System.out.println(s1==s2 );//false,常量池里面的值不相等
    }
   
    
}

我们运行之后如下所示:

 测试题5:

我们设计的代码如下所示: 

package com.ypl.String;

public class StringExercise04 {
    public static void main(String[] args) {
        Person p1=new Person();
        p1.name="rgfedu";
        Person p2 = new Person();
        p2.name="rgfedu";
        System.out.println(p1.name.equals(p2.name)); //true,两个值相等
        System.out.println(p1.name== p2.name);//true,指向两个同一样的常量,p1.name和p2.name不是new的,而是直接给的常量值
        //对象的属性都放在了堆里面。而name作为属性,他的常量值是放在常量池里面的,属性值为常量值的地址值
        System.out.println(p1.name=="rgfedu");//true,与常量池里面的一样。
        String s1 = new String("bcde");
        String s2 = new String("bcde");
        System.out.println(s1==s2);//都是new出来的,即堆里面的value指向常量池里面的同一个值。但是堆的地址不同,即为false.
    }
}
class  Person{
    String name;
    int  age;

    public Person() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

我们运行之后如下所示:

字符串的特性:

(1)String是一个final类,代表不可变的字符序列

(2)字符串是不可变的。一个字符串对象一旦被分配,其内容是不可变的。

我们思考如下问题:

(1)以下语句创建了几个对象?

String s1="hello";
s1="haha";

由于字符串是不可变得,所以我们刚开始创建的指向hello对象的s1,在创建haha对象之后,s1不再指向hello,而是指向haha。所以是创建了两个对象。

(2)

String a="hello"+"abc";

创建了几个对象?

String a="hello"+"abc";  //==>优化等价 String  a="helloabc"

创建了一个对象。

分析:

1.编译器很聪明,做一个优化,判断创建的常量池对象,是否有引用指向

2.String  a="hello"+"abc";==>String a="helloabc"

(3)

String a="hello"; //创建a对象
String b="abc";  //创建b对象
String c=a+b;

创建了几个对象?

我们通过具体的debug来进行查看:

1.

2. 我们step into追进去之后如下所示:

 我们发现我们进入了StringBuilder的构造器里面了,第一步为先创建了一个StringBuilder sb=StringBuilder();sb是在堆中。

3.执行sb.append("hello");其中append是在原来字符串的基础上追加的

 

 3.我们跳出去再继续追进去如下所示:

执行sb.append("abc");

 4.我们再跳出去再追进去:

调用sb.toString()方法,

 5.此时c的内容为helloabc了。

 最后其实是c指向堆中的对象(String)value[  ]-->池中的”helloabc"

一共有三个对象,如上所示。

我们进行测试如下:

package com.ypl.String;

public class StringExercise05 {
    public static void main(String[] args) {
        String a="hello"; //创建a对象
        String b="abc";  //创建b对象
        String d="helloabc";
        String c=a+b;
        System.out.println(c==d);
    }
}

运行之后如下所示:

 重要规则:String ci="ab"+"cd";常量相加,看的是池。

String c1=a+b;变量相加,是在堆中。

测试如下所示:
 

package com.ypl.String;

/**
 * String的实例化方式:
 * 方式一:通过字面量定义的方式
 * 方式二:通过new +构造器的方式
 * 常量与常量的拼接结果在常量池。且常量池中不会存在相同内容的常量
 * 只要其中有一个是变量,结果就在堆中
 * 如果拼接的结果调用intern()方法,返回值就在常量池中。
 */
public class String02 {
    public static void main(String[] args) {
               String s1="javaEE";
               String s2="hadoop";

               String s3="javaEEhadoop"; //常量池值指向s3
               String s4="javaEE"+"hadoop";//
        //变量名参与了此时就不在常量池了,而是在堆空间中。
               String s5=s1+"hadoop";
               String s6="javaEE"+s2;
               String s7=s1+s2;
        System.out.println(s3==s4);//true
        System.out.println(s3==s5);//false
        System.out.println(s3==s6);//false
        System.out.println(s5==s6);//false
        System.out.println(s3==s7);//false
        System.out.println(s5==s7);//false
        System.out.println(s6==s7);//false

        String s8 = s5.intern();
        //返回值得到的s8使用的常量值中已经存在的”javaEEhadoop“
        System.out.println(s3==s8);//true
        String s9 = s6.intern();
        //返回值得到的s8使用的常量值中已经存在的”javaEEhadoop“
        System.out.println(s3==s9);//true
        String s10 = s7.intern();
        //返回值得到的s8使用的常量值中已经存在的”javaEEhadoop“
        System.out.println(s3==s10);//true


        String ss1="javaEEhadoop";
        String ss2="javaEE";
        String ss3=ss2+"hadoop";
        System.out.println(ss1==ss3);  //false
        final String ss4="javaEE";  //加final时为常量,加常量时则在常量池里面(ss4:常量)
        String ss5=ss4+"hadoop";
        System.out.println(ss1==ss5); //true
    }
}

运行如下所示:

(4)

package com.ypl.String;

public class StringExercise06 {
    public static void main(String[] args) {
        String  s1="rgfedu"; //s1指向常量池中的"rgfedu"。
        String  s2="java";  //s2指向常量池中的"java"。
        String  s5="rgfedujava";  //s5指向常量池中的"rgfedujava"。
        String  s6=(s1+s2).intern(); //s6指向池中的"rgfedujava"。
        //调用intern方法时,如果池已包含等于此String方法确定的String对象的字符串(用equals(Object)方法确定),
        // 则返回池中的字符串。否则,将此String对象添加到池中,并返回对此String对象的引用。
        System.out.println(s5==s6); //true
        System.out.println(s5.equals(s6));  //true
    }
}

 我们运行之后如下所示:

(5) 下列程序运行的结果是什么,

我们的代码如下所示:

package com.ypl.String;

public class StringExercise07 {
    String str=new String("rgf");
    final  char[] ch={'j','a','v','a'};
    public void change(String str,char ch[]) {
        str="java";
        ch[0]='h';
    }

    public static void main(String[] args) {
        StringExercise07 ex=new StringExercise07();
        ex.change(ex.str,ex.ch);
        System.out.print(ex.str+" and "); //rgf
        System.out.println(ex.ch);//hava
    }
}

 我们运行之后如下所示:

我们了解一些JVM的基本知识:

三种JVM:

Sun公司的HotSpot

BEA公司的JRockit

IBM公司的J9 VM

堆内示意图:

伊甸区
幸存0区
幸存1区
养老区
永久存储区

 一个JVM实例只存在一个堆内存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,保存所有引用类型的真实信息,以方便执行器执行,堆内存分为三部分:

新生区、养老区、永久存储区(方法区)

虽然JVM规范将方法区描述为堆的一个逻辑部分,但它却还有一个别名叫做Non-Heap(非堆),目的就是要和堆分开。

JDK1.6的时候,常量池在方法区,JDK1.7的时候,常量池放在了堆里面。JDK1.8的时候,常量池又拿回到方法区了。

(6)

 String类的常用方法:

String类是保存字符串常量的。每次更新都需要重新开辟空间,(创建对象,重新指向)效率极低,因此java设计者还提供了StringBuilder和StringBuffer来增强String的功能,并提高效率。

 equals  //区分大小写,判断内容是否相等

equalsIgnoreCase  //忽略大小写的判断内容是否相等

length //获取字符的个数,字符串的长度

indexOf  //获取字符在字符串中第1次出现的索引,索引从0开始,如果找不到,返回-1

lastIndexOf  //获取字符在字符串中最后一次出现的索引,索引从0开始,如找不到,返回-1

substring  //截取指定范围的子串

trim  //去前后空格

charAt:获取某索引处的字符,注意不能使用Str[index]这种方式。

isEmpty():判断是否是空字符串

我们设计的代码如下所示:

package com.ypl.String;

public class StringMethod01 {
    public static void main(String[] args) {
        //1.equals,比较内容是否相等,区分大小写
        String str1="hello";
        String str2="Hello";
        System.out.println(str1.equals(str2));  //false
        //2.equalsIgnoreCase 忽略大小写的判断内容是否相等
        String str3="hello";
        String str4="Hello";
        System.out.println(str3.equalsIgnoreCase(str4));  //true
        String username="Rgf";
        if("rgf".equalsIgnoreCase(username)){
            System.out.println("Success!");
        }else {
            System.out.println("Failure");
        }
        //3.length 获取字符的个数,字符串的长度
        System.out.println("rgf".length());//3
        //4.indexOf获取字符在字符串对象中第一次出现的索引,索引从0开始,如果找不到,返回-1.
        String s1="wer@ter";
        int index=s1.indexOf('r');
        System.out.println(index);//2
        //5.lastIndexOf获取字符在字符串中最后一次出现的索引,索引从0开始,如果找不到,返回-1.
        String s2="wer@ter";
        int inde=s2.lastIndexOf('r');
        System.out.println(inde);//6
        //6.substring 截取指定范围的子串
        String name="hello,rgf";
        //从索引0开始截取2前面的内容
        System.out.println(name.substring(0,2));
        //从索引6开始截取后面所有的内容
        System.out.println(name.substring(6));  //截取后面的字符
        //7.charAt:获取某索引处的字符,注意不能使用Str[index]这种方式。
        System.out.println(s1.charAt(0));
        System.out.println(s1.charAt(6));
        //8.isEmpty:判断是否是空字符串
        /**
         *  public boolean isEmpty() {
         *         return value.length == 0;
         *     }
         */
        System.out.println(s1.isEmpty());
        //9.trim:返回字符串的副本,忽略前导空白和尾部空白
        String s2="   he  llo  world   ";
        String s3 = s2.trim();
        System.out.println(s2);
        System.out.println(s3);
    }
}

我们运行之后如下所示:

 我们设计的代码如下所示:

       //1.indexOf获取字符在字符串对象中第一次出现的索引,索引从0开始,如果找不到,返回-1.
        String s1="wer@ter";
        System.out.println("weIndexOf="+s1.indexOf("we")); //0

 我们运行之后如下所示:

其他常用方法:

toUpperCase  //转换成大写

toLowerCase  //转换成小写

concat  //将指定字符串连接到此字符串的结尾,等价于用“+”。

replace  替换字符串中的字符

split  分割字符串,对于某些分割字符,我们需要转义比如 | \\等

compareTo  //比较两个字符串的大小

toCharArray  //转换成字符数组

format  //格式字符串,%s字符串    %c字符  %d 整型  %.2f浮点型

我们设计的代码如下所示:  

package com.ypl.String;

public class StringMethod02 {
    public static void main(String[] args) {
         //1.toUpperCase  ,转换成大写
        String s="heLLo";
        System.out.println(s.toUpperCase()); //HELLO
        //2.toLowerCase,转换成小写
        System.out.println(s.toLowerCase()); //hello
      // 3.concat拼接字符串
        String  s1="宝玉";
        s1=s1.concat("林黛玉").concat("薛宝钗").concat("together");
        System.out.println(s1);
       //4.replace替换字符串中的字符 ,replaceAll:替换所有
        String s2="宝玉 and 薛宝钗 薛宝钗 薛宝钗";
        //s2.replace()方法执行后,返回的结果才是替换过的。
        //注意对s1没有任何影响
        s2=s2.replace("薛宝钗","林黛玉");
        System.out.println(s2);

        //5.split分割字符串,对于某些分割字符,我们需要转义比如 | \\等
        String poem="锄禾日当午,汗滴河下土,谁知盘中餐,粒粒皆辛苦";
        //以,为标准对这首诗进行分割,就返回一个数组。
        String[] split=poem.split(",");
        System.out.println("==这首诗的内容是==");
         for(int i=0;i<split.length;i++){
               System.out.println(split[i]);
         }
         //在对字符串进行分割的时候,如果有特殊字符,需要加入转义符\  ,如下\\\\,\转义符 \  \转义符  \.
         String poem1="E:\\aaa\\bbb";
        String[] split1 = poem1.split("\\\\");
        for (int j=0;j<split1.length;j++){
            System.out.println(split1[j]);
        }
        //6.toCharArray 转换成字符数组
        s="happy";
        char[] chs = s.toCharArray();
        for (int i=0;i<chs.length;i++){
            System.out.println(chs[i]);
        }
        //7.compareTo 比较两个字符串的大小,如果前者大,则返回正数,后者大,则返回负数,如果相等,返回0
        //(1)如果长度相同,并且每个字符也相同,就返回0.
        //(2)如果长度相同或者长度不相同,但是在进行比较时可以区分大小,就返回的是
        // if(c1!=c2){
        //return  c1-c2;
        //}
        //即从不相同的字符开始相减。
        //(4)如果长度不同,如果前面的部分都相同,就返回str1.len-str2.len
        String a="jchn";
        String b="jack";
        System.out.println(a.compareTo(b)); //返回值是'c'-'a'=2的值
        //我们进行查看源码如下所示:
        /*
         public int compareTo(String anotherString) {
        int len1 = value.length;
        int len2 = anotherString.value.length;
        int lim = Math.min(len1, len2);
        char v1[] = value;
        char v2[] = anotherString.value;
        int k = 0;
        while (k < lim) {
            char c1 = v1[k];
            char c2 = v2[k];
            if (c1 != c2) {
                return c1 - c2;
            }
            k++;
        }
        return len1 - len2;
    }
         */
        
      //8.format格式字符串
        //占位符有:
        // %s 字符串 %c 字符 %d 整型  %.2f浮点型
        String name="john";
        int age=10;
        double score=98.3/3;
        char gender='男';
        //将所有信息都拼接在一个字符串
        String info="我的姓名是"+name+"年龄是"+age+",成绩是"+score+"性别是"+gender+"。希望大家喜欢我";
        System.out.println(info);
        //(1)%s,%d,%.2f,%c称为占位符。
        //(2)这些占位符由后面的变量来替换
        //(3)%s表示后面由字符串来进行替换
        //(4)%d是整数来替换
        //(5)%.2f表示使用小数来替换,替换后只会保留小数点两位,并且进行四舍五入的处理。
        //(6)%c使用char类型来替换
        String info2=String.format("我的姓名是%s年龄是%d,成绩是%.2f性别是%c.希望大家喜欢我!",name,age,score,gender);
        System.out.println("info="+info2);
    }
}

我们运行之后如下所示:

我们继续演示其他方法:

package com.ypl.String;

public class StringMethod00 {
    public static void main(String[] args) {
        /**
         * endsWith:测试此字符串是否以指定的后缀结束
         * startsWith:测试此字符串是否以指定的前缀结束
         * startsWith:测试此字符串从指定索引开始的子字符串是否以指定前缀开始
         * contains:当且仅当此字符串包含指定的char值序列时,返回true
         * matches:告知此字符串是否匹配给定的正则表达式。
         */

        String str1="helloworld";
        boolean b1 = str1.endsWith("ld");
        System.out.println(b1);
        boolean b2 = str1.startsWith("He");
        System.out.println(b2);
        boolean b3 = str1.startsWith("ll", 2);
        System.out.println(b3);

        String str2="wo";
        System.out.println(str1.contains(str2));

        String str3="hellorworld";
        System.out.println(str3.lastIndexOf("or"));
        System.out.println(str3.lastIndexOf("or",5));
          //什么情况下,indexof(str)和lastIndexof(str)返回值相同?
        //情况一:存在唯一的一个str,情况二:不存在str.


        String str4="12345";
        //判断str4字符串中是否全部有数字组成,即有1-n个数字组成
        boolean matches = str4.matches("\\d+");
        System.out.println(matches);

    }
}

运行之后如下所示:

 Java比较器

在java中经常会涉及到对象数组的排序问题,那么就涉及到对象之间的比较问题

java实现对象排序的方式有两种:

自然排序:java.lang.Comparable

定制排序:java.util.Comparator

自然排序:

package com.ypl.data_;

import org.junit.jupiter.api.Test;

import java.util.Arrays;

/**
 * 一、说明:Java中的对象,正常情况下,只能进行比较操作:==或!=,不能使用>或<的。
 *    但是在开发场景中,我们需要对多个对象进行排序,言外之意,就需要比较对象的大小。
 *    如何实现?使用两个接口中的任何一个:Comparable 或 Comparator
 * 二、Comparable接口的使用
 *
 *
 */
public class CompareTest {
    /*
    Comparable接口的使用举例:自然排序
    1.像String、包装类等实现了Comparable接口,重写了compareTo(obj)方法的,给出了比较两个对象大小的方式
    2.像String、包装类重写重写了compareTo(obj)方法以后,进行了从小到大的排列。
    3.重写compareTo(obj)的规则:
    如果当前对象this大于形参对象obj,则返回正整数,
    如果当前对象this小于形参对象obj,则返回负整数
    如果当前对象this等于形参对象obj,则返回零
    4.对于自定义类来说,如果需要排序,我们可以让自定义类实现Comparable接口,重写compareTo(obj),
    在compareTo(obj)方法中指明如何排序。
    源码如下所示:
    public int compareTo(String anotherString) {
        int len1 = value.length;
        int len2 = anotherString.value.length;
        int lim = Math.min(len1, len2);
        char v1[] = value;
        char v2[] = anotherString.value;
        int k = 0;
        while (k < lim) {
            char c1 = v1[k];
            char c2 = v2[k];
            if (c1 != c2) {
                return c1 - c2;
            }
            k++;
        }
        return len1 - len2;
    }
     */
    public static void main(String[] args) {
        String[] arr=new String[]{"AA","CC","KK","MM","GG","JJ","DD"};
        Arrays.sort(arr);
        //String实现了Comparable接口,实现该接口要重写compareTo方法,sort底层为compareTo方法。
        System.out.println(Arrays.toString(arr));
    }
    @Test
    public  void  test2(){
        Goods[] arr = new Goods[5];
        arr[0]=new Goods("lenovoMouse",34);
        arr[1]=new Goods("dellMouse",43);
        arr[2]=new Goods("XiaomiMouse",12);
        arr[3]=new Goods("huaweiMouse",65);
        arr[4]=new Goods("MicrosoftMouse",43);
        Arrays.sort(arr);
        System.out.println(Arrays.toString(arr));
    }
}
package com.ypl.data_;

/**
 * 商品类
 */
public class Goods implements  Comparable{
    private String name;
    private double price;

    public Goods() {
    }

    public Goods(String name, double price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Goods{" +
                "name='" + name + '\'' +
                ", price=" + price +
                '}';
    }
//指明商品比较大小的方式:按照价格从低到高排序,再按照产品名称从高到低排序
    @Override
    public int compareTo(Object o) {
      if(o instanceof Goods){
          Goods goods=(Goods) o;
          //方式一:
          if(this.price>goods.price){
              return 1;
          }else if(this.price<goods.price){
              return  -1;
          }else {
           //   return 0;
             return this.name.compareTo(goods.name);
          }
          //方式二:
     //     return Double.compare(this.price,goods.price);
      }
      throw  new RuntimeException("传入的数据类型不一致!");
    }
}

 运行之后如下所示:

 

 常见算法题目:

1.模拟一个trim方法,去除字符串两端的空格:

2.将一个字符串进行反转。将字符串中指定部分进行反转。比如“abcdefg"反转为”abfedcg"

3.获取一个字符串在另一个字符串中出现的次数。

比如:获取“ab"在”abkkcadkabkebfkabkskab"中出现的次数(indexof、contence|substring)

4.获取两个字符串中最大相同子串。比如:

str1="abcwerthelloyuiodef",str2="cvhellobnm"

提示:将短的那个串进行长度依次递减的子串与较长的串比较

5.对字符串中字符进行自然顺序排序

提示:

(1)字符串变成字符数组

(2)对数组排序,选择,冒泡,Arrays.sort();

(3)将排序后的数组变成字符串。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一直再追梦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值