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!"
下面是一个例子:
public class StringDemo {
public static void main(String args[]) {
String string1 = "saw I was ";
System.out.println("Dot " + string1 + "Tod");
}
}
以上实例编译运行结果如下:
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代表不同的日期与时间转换符 |
测试用例:
public static void main(String[] args) {
String str=null;
str=String.format("Hi,%s", "王力");
System.out.println(str);
str=String.format("Hi,%s:%s.%s", "王南","王力","王张");
System.out.println(str);
System.out.printf("字母a的大写是:%c %n", 'A');
System.out.printf("3>7的结果是:%b %n", 3>7);
System.out.printf("100的一半是:%d %n", 100/2);
System.out.printf("100的16进制数是:%x %n", 100);
System.out.printf("100的8进制数是:%o %n", 100);
System.out.printf("50元的书打8.5折扣是:%f 元%n", 50*0.85);
System.out.printf("上面价格的16进制数是:%a %n", 50*0.85);
System.out.printf("上面价格的指数表示:%e %n", 50*0.85);
System.out.printf("上面价格的指数和浮点数结果的长度较短的是:%g %n", 50*0.85);
System.out.printf("上面的折扣是%d%% %n", 85);
System.out.printf("字母A的散列码是:%h %n", 'A');
}
输出结果:
Hi,王力
Hi,王南:王力.王张
字母a的大写是:A
3>7的结果是:false
100的一半是:50
100的16进制数是:64
100的8进制数是:144
50元的书打8.5折扣是:42.500000 元
上面价格的16进制数是:0x1.54p5
上面价格的指数表示:4.250000e+01
上面价格的指数和浮点数结果的长度较短的是:42.5000
上面的折扣是85%
字母A的散列码是:41
String、StringBuffer和StringBuider
首先,通过阅读Java源码我们可以发现String类是被final标记为不能继承的类,其内部是使用一个char数组来记录整个字符串的值,而String类的大部分改变数据的方法(如substring、concat和replace等),都是直接创建一个新的String对象改变后返回,所以我们说String的数据是不能更改的,实际上Java也没有为String设计更改其内部char数组的方法。
在这里要永远记住一点:“对String对象的任何改变都不影响到原对象,相关的任何change操作都会生成新的对象”。
看个例子
public class Main {
public static void main(String[] args) {
String str1 = "hello world";
String str2 = new String("hello world");
String str3 = "hello world";
String str4 = new String("hello world");
System.out.println(str1==str2);
System.out.println(str1==str3);
System.out.println(str2==str4);
}
}
输出的结果是:
false
true
false
为什么会出现这样的结果?下面解释一下原因:
对于JVM内存机制,在class文件中有一部分 来存储编译期间生成的 字面常量以及符号引用,这部分叫做class文件常量池,在运行期间对应着方法区的运行时常量池。
因此在上述代码中,String str1 = "hello world";和String str3 = "hello world"; 都在编译期间生成了字面常量和符号引用,运行期间字面常量"hello world"被存储在运行时常量池(当然只保存了一份)。通过这种方式来将String对象跟引用绑定的话,JVM执行引擎会先在运行时常量池查找是否存在相同的字面常量,如果存在,则直接将引用指向已经存在的字面常量;否则在运行时常量池开辟一个空间来存储该字面常量,并将引用指向该字面常量。
总所周知,通过new关键字来生成对象是在堆区进行的,而在堆区进行对象生成的过程是不会去检测该对象是否已经存在的。因此通过new来创建对象,创建出的一定是不同的对象,即使字符串的内容是相同的。
StringBuilder
我们看一下下面的例子:
public class Main {
public static void main(String[] args) {
String string = "";
for(int i=0;i<10000;i++){
string += "hello";
}
}
}
我们知道,每次对String进行连接操作都是重新生成一个新的String,那么这段代码运行后会创建10001个String对象,而存在引用的仅1个String对象,其他对象会被当做垃圾回收,造成了内存和CPU的浪费。
Java为了解决这种情况,设计了StringBuilder,StringBuilder可以直接修改其内部记录的char数组,在改变字符串时不会生成多余的对象,如下:
public class Main {
public static void main(String[] args) {
StringBuilder stringBuilder = new StringBuilder();
for(int i=0;i<10000;i++){
stringBuilder.append("hello");
}
}
}
得到的结果和上面使用String的方法一致,但避免了内存和CPU资源的浪费。
StringBuffer
实际上StringBuffer提供的功能和StringBuilder是一样的,唯一的不同就是StringBuilder是线程不安全的,只能在单线程中使用,而StringBuffer是线程安全的,可以在多线程中使用。
Java学习笔记(二):String的更多相关文章
- Java学习笔记五--String(二)String其他方法
第一节课 // 清除单位字符串开始和结尾空白的副本 String.trim(); 字符串每次更改都会创建新的对象,而不会覆盖原来的字符串,每次拼接都会产生新的String对象,耗时耗内存. java. ...
- Java学习笔记二十九:一个Java面向对象的小练习
一个Java面向对象的小练习 一:项目需求与解决思路: 学习了这么长时间的面向对象,我们只是对面向对象有了一个简单的认识,我们现在来做一个小练习,这个例子可以使大家更好的掌握面向对象的特性: 1.人类 ...
- 数组排序、递归——(Java学习笔记二)
升序: 选择排序: 选定一个元素,一次和后面的元素相比较,如果选定的元素大雨后面的比较元素,就交换位置 先出现最小值,最后出现最大值. public stat ...
- Java学习笔记二十八:Java中的接口
Java中的接口 一:Java的接口: 接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明.一个类通过继承接口的方式,从而来继承 ...
- Java学习笔记二十七:Java中的抽象类
Java中的抽象类 一:Java抽象类: 在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就 ...
- Java学习笔记二十五:Java面向对象的三大特性之多态
Java面向对象的三大特性之多态 一:什么是多态: 多态是同一个行为具有多个不同表现形式或形态的能力. 多态就是同一个接口,使用不同的实例而执行不同操作. 多态性是对象多种表现形式的体现. 现实中,比 ...
- Java学习笔记二十四:Java中的Object类
Java中的Object类 一:什么是Object类: Object类是所有类的父类,相当于所有类的老祖宗,如果一个类没有使用extends关键字明确标识继承另外一个类,那么这个类默认继承Object ...
- Java学习笔记二十二:Java的方法重写
Java的方法重写 一:什么是方法的重写: 如果子类对继承父类的方法不满意,是可以重写父类继承的方法的,当调用方法时会优先调用子类的方法. 语法规则 返回值类型.方法名.参数类型及个数都要与父类继承的 ...
- Java学习笔记二十一:Java面向对象的三大特性之继承
Java面向对象的三大特性之继承 一:继承的概念: 继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类. 继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方 ...
- Java学习笔记二十:Java中的内部类
Java中的内部类 一:什么是内部类: (1).什么是内部类呢? 内部类( Inner Class )就是定义在另外一个类里面的类.与之对应,包含内部类的类被称为外部类. (2).那为什么要将一个类定 ...
随机推荐
- Codeforces_GYM Flight Boarding Optimization
(ACM ICPC 2013–2014, NEERC, Northern Subregional Contest) Flight Boarding OptimizationInput file: fl ...
- 50个python库
50个很棒的Python模块,包含几乎所有的需要:比如Databases,GUIs,Images, Sound, OS interaction, Web,以及其他.推荐收藏. Graphical in ...
- [反汇编练习] 160个CrackMe之004
[反汇编练习] 160个CrackMe之004. 本系列文章的目的是从一个没有任何经验的新手的角度(其实就是我自己),一步步尝试将160个CrackMe全部破解,如果可以,通过任何方式写出一个类似于注 ...
- 通过对源代码的反向工程学习CoreData架构
在本文开始,先给出反向工程后的结果: 不过需要注意,三个实例的指针都被同一个实例拥有,比如三个指针都位于appDelegate. 在AppDelegate类中定义了下面三个属性: @property ...
- SQL SERVER 实现分组合并实现列数据拼接
需求场景: SQL SERVER 中组织的数据结构是一个层级关系,现在需要抓出每个组织节点以上的全部组织信息,数据示例如下: ADOrg_ID--------------ParentID------- ...
- Linux Systemd——在RHEL/CentOS 7中启动/停止/重启服务
RHEL/CentOS 7.0中一个最主要的改变,就是切换到了systemd.它用于替代红帽企业版Linux前任版本中的SysV和Upstart,对系统和服务进行管理.systemd兼容SysV和Li ...
- ARM体系的异常中断
在ARM体系中,通常有3种方式控制处理器的流程 1:在正常执行过程中,每执行一条ARM指令,程序计数器寄存器PC的值加四个字节,在每执行一条Thumb指令,程序计数器寄存器PC的值加两个字节,整个过 ...
- 你认为你很了解Javascript?
(翻译不当之处请谅解) 来源:http://www.ido321.com/914.html 这里有5个小脚本,有助于你真正理解JavaScript核心–闭包和作用域.没有在控制台运行之前,尝试回答每个 ...
- cocos2d-x 不能在android真机debug的问题
最近在做cocos2d-x开发的时候,发现在android真机上不能调试C++代码,显示如下警告信息 Ignoring packet error, continuing... warning: unr ...
- Tkinter教程之Event篇(3)
本文转载自:http://blog.csdn.net/jcodeer/article/details/1823550 '''Tkinter教程之Event篇(3)''''''11.两个事件同时绑定到一 ...