Java学习笔记(二):String

String

在Java中String是作为引用对象存在的一种数据类型,用来保存字符串。

实例化和赋值

//直接声明
String s1 = "Hello world!";
//通过构造函数创建, 提供了 11 种不同参数创建的方法
char[] c = { ‘h‘, ‘e‘, ‘l‘, ‘l‘, ‘o‘, ‘.‘};
String s2 = new String(c);

String类是不可改变的,所以你一旦创建了String对象,那它的值就无法改变了。如果需要对字符串做很多修改,那么应该选择使用StringBuffer或StringBuilder类。

我们来看下面的另一种创建方法:

使用这种方式来创建字符串,在堆内存中会创建两个字符串对象,因为当使用""符号是就表示创建了一个新的字符串,而str只指向了第二个对象,导致第一个对象没有引用,处于等待回收的状态,除了加大内存使用外,还增加了GC的负担,所以直接使用""来创建字符串是更好的方式。

连接字符串

String类提供了连接两个字符串的方法:

string1.concat(string2);

返回string2连接string1的新字符串。也可以对字符串常量使用concat()方法,如:

"My name is ".concat("Zara");

更常用的是使用‘+‘操作符来连接字符串,如:

"Hello," + " world" + "!"

结果如下:

"Hello, world!"

下面是一个例子:

1 public class StringDemo {
2    public static void main(String args[]) {
3    String string1 = "saw I was ";
4    System.out.println("Dot " + string1 + "Tod");
5 }
6 }

以上实例编译运行结果如下:

Dot saw I was Tod

比较字符串

判断字符串内容是否相等,Java和大部分语言不一样,Java中==是比较两个字符串引用的地址是否相同,即是否指向同一个对象,而equals方法则比较字符串的内容是否相同。

String a = "abc";
String b = new String("abc");

a == b返回false,a.equals(b)返回true。这时创建b时不管"abc"是否存在都会new一个新的"abc",从而a和b指向的字符创对象是不同的,因此返回false。

我们来看下面的情况,容易混淆:

String a = "abc";
String b = "abc";

a == b返回true,a.equals(b)同样返回true,这是为什么呢?

原来程序在运行时有一个字符串池,创建字符串时会先查找池中是否有相应的字符串,如果已经存在的话只需把引用指向它即可,如果没有则新建一个。

上例中创建a时,会在字符串池中首先创建一个"abc",然后a指向它;创建b时,由于"abc"已经存在,b直接指向它即可。

格式化字符串

String类的format()方法用于创建格式化的字符串以及连接多个字符串对象,

format()方法有两种重载形式:

  • format(String format, Object... args) 新字符串使用本地语言环境,制定字符串格式和参数生成格式化的新字符串。
  • format(Locale locale, String format, Object... args) 使用指定的语言环境,制定字符串格式和参数生成格式化的字符串。

显示不同转换符实现不同数据类型到字符串的转换,如图所示:


转  换  符


说    明


示    例


%s


字符串类型


"mingrisoft"


%c


字符类型


‘m‘


%b


布尔类型


true


%d


整数类型(十进制)


99


%x


整数类型(十六进制)


FF


%o


整数类型(八进制)


77


%f


浮点类型


99.99


%a


十六进制浮点类型


FF.35AE


%e


指数类型


9.38e+5


%g


通用浮点类型(f和e类型中较短的)

 

%h


散列码

 

%%


百分比类型



%n


换行符

 

%tx


日期与时间类型(x代表不同的日期与时间转换符

测试用例:

 1 public static void main(String[] args) {
 2     String str=null;
 3     str=String.format("Hi,%s", "王力");
 4     System.out.println(str);
 5     str=String.format("Hi,%s:%s.%s", "王南","王力","王张");
 6     System.out.println(str);
 7     System.out.printf("字母a的大写是:%c %n", ‘A‘);
 8     System.out.printf("3>7的结果是:%b %n", 3>7);
 9     System.out.printf("100的一半是:%d %n", 100/2);
10     System.out.printf("100的16进制数是:%x %n", 100);
11     System.out.printf("100的8进制数是:%o %n", 100);
12     System.out.printf("50元的书打8.5折扣是:%f 元%n", 50*0.85);
13     System.out.printf("上面价格的16进制数是:%a %n", 50*0.85);
14     System.out.printf("上面价格的指数表示:%e %n", 50*0.85);
15     System.out.printf("上面价格的指数和浮点数结果的长度较短的是:%g %n", 50*0.85);
16     System.out.printf("上面的折扣是%d%% %n", 85);
17     System.out.printf("字母A的散列码是:%h %n", ‘A‘);
18 }  

输出结果:

 1 Hi,王力
 2 Hi,王南:王力.王张
 3 字母a的大写是:A
 4 3>7的结果是:false
 5 100的一半是:50
 6 100的16进制数是:64
 7 100的8进制数是:144
 8 50元的书打8.5折扣是:42.500000 元
 9 上面价格的16进制数是:0x1.54p5
10 上面价格的指数表示:4.250000e+01
11 上面价格的指数和浮点数结果的长度较短的是:42.5000
12 上面的折扣是85%
13 字母A的散列码是:41   

String、StringBuffer和StringBuider

首先,通过阅读Java源码我们可以发现String类是被final标记为不能继承的类,其内部是使用一个char数组来记录整个字符串的值,而String类的大部分改变数据的方法(如substring、concat和replace等),都是直接创建一个新的String对象改变后返回,所以我们说String的数据是不能更改的,实际上Java也没有为String设计更改其内部char数组的方法。

在这里要永远记住一点:“对String对象的任何改变都不影响到原对象,相关的任何change操作都会生成新的对象”。

看个例子

 1 public class Main {
 2          
 3     public static void main(String[] args) {
 4         String str1 = "hello world";
 5         String str2 = new String("hello world");
 6         String str3 = "hello world";
 7         String str4 = new String("hello world");
 8          
 9         System.out.println(str1==str2);
10         System.out.println(str1==str3);
11         System.out.println(str2==str4);
12     }
13 }

输出的结果是:

1 false
2 true
3 false

为什么会出现这样的结果?下面解释一下原因:

对于JVM内存机制,在class文件中有一部分 来存储编译期间生成的 字面常量以及符号引用,这部分叫做class文件常量池,在运行期间对应着方法区的运行时常量池。

因此在上述代码中,String str1 = "hello world";和String str3 = "hello world"; 都在编译期间生成了字面常量和符号引用,运行期间字面常量"hello world"被存储在运行时常量池(当然只保存了一份)。通过这种方式来将String对象跟引用绑定的话,JVM执行引擎会先在运行时常量池查找是否存在相同的字面常量,如果存在,则直接将引用指向已经存在的字面常量;否则在运行时常量池开辟一个空间来存储该字面常量,并将引用指向该字面常量。

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

StringBuilder

我们看一下下面的例子:

1 public class Main {
2     public static void main(String[] args) {
3         String string = "";
4         for(int i=0;i<10000;i++){
5             string += "hello";
6         }
7     }
8 }

我们知道,每次对String进行连接操作都是重新生成一个新的String,那么这段代码运行后会创建10001个String对象,而存在引用的仅1个String对象,其他对象会被当做垃圾回收,造成了内存和CPU的浪费。

Java为了解决这种情况,设计了StringBuilder,StringBuilder可以直接修改其内部记录的char数组,在改变字符串时不会生成多余的对象,如下:

1 public class Main {
2     public static void main(String[] args) {
3         StringBuilder stringBuilder = new StringBuilder();
4         for(int i=0;i<10000;i++){
5             stringBuilder.append("hello");
6         }
7     }
8 }

得到的结果和上面使用String的方法一致,但避免了内存和CPU资源的浪费。

StringBuffer

实际上StringBuffer提供的功能和StringBuilder是一样的,唯一的不同就是StringBuilder是线程不安全的,只能在单线程中使用,而StringBuffer是线程安全的,可以在多线程中使用。

时间: 2024-07-30 13:28:59

Java学习笔记(二):String的相关文章

Java学习笔记二:初始化(一)

1.对象初始化 在使用 new 初始化对象时,系统通常使用默认的构造函数,这个构造函数没有返回值,格式为: public class InitializeTest { private int a; // 默认构造函数 public InitializeTest() { } // 自定义构造函数 public InitializeTest(int a) { this.a = a; } } 虽然在使用 new 创建实例时,返回了这个对象的引用,但是这个与没有返回值的void方法不同 //void方法

Java学习笔记二:数据类型

Java学习笔记二:数据类型 1. 整型:没有小数部分,允许为负数,Java整型分4种:int short long byte 1.1 Int最为常用,一个Int类型变量在内存中占用4个字节,取值范围从-2 147 483 6至2 147 483 647 超过20亿,如果用来存储大于20亿的值,最好使用long型. 1.2  int 与Integer: Java中的数据类型分为基本数据类型和复杂数据类型.Int为前者,integer为后者. Integer是int的封装类,提供了很多转换方法,当

Java学习笔记二:初始化(二)

这里主要学习初始化,分为: 1.变量初始化 2.构造函数初始化 3.数组初始化 4.继承初始化   1.变量初始化 成员初始化分为两种: (1)成员变量初始化 在定义成员变量时,如果没有给变量赋于初始值,系统会给出一个默认值. 这里有两种选择:要么使用系统的默认初始值,要么自己定义初始值. 系统给出的默认输出值为: boolean false char [ ] byte 0 short 0 int 0 long 0 float 0.0 double 0.0 refenece null char初

数组排序、递归——(Java学习笔记二)

升序: 选择排序: 选定一个元素,一次和后面的元素相比较,如果选定的元素大雨后面的比较元素,就交换位置 先出现最小值,最后出现最大值. public static void main(String[] args) { int[] arr = {5,8,9,12,55,565,421,12,2512,-5,-56}; sortMethod(arr); p(arr); } //排序核心代码 private static void sortMethod(int arr[]) { for(int i =

Java学习笔记二:初始化(三)

3.数组初始化 1.数组是一个固定长度的集合,在初始化大小后就不能改变其长度. 数组分配在堆中,并在栈中存有指向该数组的一个引用. class Arr { public static void main(String[] args) { int[] a = {1, 2, 3, 4}; int[] b = a; for (int i = 0; i < b.length; i++) { b[i]++; } for (int i = 0; i < a.length; i++) { System.ou

JAVA学习笔记(二)

简单的做了个登录界面,运用了添加事件响应,但添加单选框时没有显示出来. package 界面设计; import javax.swing.*; import java.awt.*; import java.awt.event.*; public class JTextFieldDemo extends JFrame implements ActionListener{ JTextField jtf=new JTextField(10); JPasswordField jpf=new JPassw

黑马程序员——JAVA学习笔记二(语法基础)

1,     Java程序都是以类的形式存在的,所以需要告诉虚拟机需要加载类的位置,那么可以设置classpath变量.classpath变量如果有;,则表示还会在当前目录查找,没有;则不会在当前目录查找. 当然如果没有classpath也会在当前目录查找. 2,    rd删除目录,加/S可以删整个目录,Windows从里向外删除. 3,    一个文件只有一个public类,文件名必须和类名相同,如果不是友好类,则不需要同名. 4,    JAVA一共有50个关键字 关键字是电脑语言里事先定

Java学习笔记二(基础语法)

1 Java 是强类型语言,提供的8中基本数据类型有以下几类: Byte :字节类型,一个字节,有符号的8位的整数.字节类型参与运算的时候,会转化为int类型. byte b;b=3;b=(byte)(b*3);  在这里第二个赋值必须经过强制类型转化,否则会编译出错. short:2个字节,Byte可行可以直接赋值给short,但是反过来需要强制类型转化. int:4字节类型的有符号的整数. long:8个字节类型的有符号的整形 char:2个字节的无符号的类型.采用的是Unicode的编码格

JAVA学习笔记(二) - 运算符

变量与常量 变量的声明和赋值 /* * 变量的声明和赋值 */ public class Test { public static void main(String[] args) { // 1-可以声明的同时,对变量赋值:int x = 10; //局部变量,变量名符合命名规则. //推荐使用此种方式. int x = 10; // 2-可以先声明,再赋值:int x; x = 10; int y;//变量的声明 y = 20;//将20赋值给y指向的内存空间 // 3-可以一行声明多个变量:

Java学习笔记之String、StringBuffer和StringBuilder

更多博文可参考我的个人博客:贱贱的梦想 基本概念 String:此类代表字符串常量,它们的值在创建之后不能更改. StringBuffer:是一个线程安全的可变字符序列,它与String一样,在内存中保存的都是一个有序的字符串序列(char类型的数组),不同点是StringBuffer对象的值是可变的. StringBuilder:与StringBuffer类基本相同,都是可变字符串系列,不同点是StringBuilder是线程不安全的. 分析 简单的说,String类型和StringBuild