日期时间类第一阶段(偏重点)
java.util.Date
里面很多过期的方法不推荐使用
java.util.Date可以精确到 年-月-日 时:分:秒
Date d = new Date()创建对象后的方法 | 输出 |
---|---|
d.toString();//直接输出d也一样 | 星期 月份 日子 时:分:秒 中国标准地区 年 Wed Jan 03 12:39:23 CST 2024 |
3 Jan 2024 04:39:23 GMT | |
d.toLocaleString(); | 2024年1月3日 下午12:39:23 |
1900+d.getYear();//1900+124 | 2024 |
d.getMonth();//返回的值在0~11之间,0代表1月,1代表2月… | 0 |
d.getDate()//日子 | 3 |
d.getDay()//星期 | 3 |
d.getTime();//返回自1970年1月1日00:00:00 GMT 以来到此Date对象表示的毫秒数。 | 1704271878105 |
System.currentTimeMillis();//和上面一样 | 1704271878157 |
下面代码了解即可
public static void main(String[] args) {
java.util.Date dt = new java.util.Date();
System.out.println(dt);//默认调用.toString()
//获得年份:
int year = dt.getYear();//方法名出现横线说明方法过期了,不推荐使用
int month = dt.getMonth();
int week = dt.getDay();
int day2 = dt.getDate();
int hours = dt.getHours();
int minutes = dt.getMinutes();
int seconds = dt.getSeconds();
long time = dt.getTime();
int timezoneOffset = dt.getTimezoneOffset();
System.out.println(dt);//Thu Jan 11 08:19:39 CST 2024CST是China Standard Time中国标准地区
System.out.println("年份:"+year);//当前距1900的差
System.out.println("月份:"+month);//当前月份-1,0是1月
System.out.println("星期:"+week);//getDay()是星期
System.out.println("日:"+day2);//getDate()是日
System.out.println("小时:"+hours);
System.out.println("分钟:"+minutes);
System.out.println("秒钟:"+seconds);
System.out.println("毫秒:"+time);//1900至今的毫秒数
System.out.println("时区偏移量:"+timezoneOffset);//时区的偏移量
System.out.println(dt.toLocaleString());//2024年1月11日 上午8:40:48
}
java.sql.Date
是java.util.Date子类
java.sql.Date只能精确到年-月-日
java.sql.Date d = new java.sql.Date(System.currentTimeMillis()); | 定义sqlDate类型 |
---|---|
.toString(); | 日期转String类型 |
java.util.Date date3 = d; | sqlDate类型转utilDate类型 |
d.valueOf(“2019-3-8”);//字符串格式不能变,下面介绍了格式 | String类型转sqlDate类型 |
其他方法和父类的一样 |
两个类要同时用的话:
java.util.Date dt = new java.util.Date();
java.sql.Date(System.currentTimeMillis());//当前系统毫秒数
看下面代码了解即可
public static void main(String[] args) {
java.sql.Date date1 = new java.sql.Date(124,0,11);//当前年-1900,月从0开始
java.sql.Date date2 = new java.sql.Date(System.currentTimeMillis());//当前系统毫秒数
System.out.println(date1);
System.out.println(date2);
}
sql.Date和util.Date之间的转换,字符串转util.Date(偏重点)
sql.Date转util.Date
- 向上类型转换(自动)
java.sql.Date sql1 = new java.sql.Date(System.currentTimeMillis());//系统时间
java.util.Date util1 = sql1;//向上自动转换
util.Date转sql.Date
-
向下类型转换(强制类型转换:(需之前向上转型过))
sql1 = (java.sql.Date)util1;//向下强制转换
-
借助构造器转换:(可没向上转型过)
java.util.Date util3 = new java.util.Date();
java.sql.Date sql2 = new java.sql.Date(util3.getTime());
String转日期
注意:“2024-1-11"参数格式必须是"年-月-日”,否则报错
java.sql.Date sql3 = java.sql.Date.valueOf(“2024-1-11”);
解决方案:
引入SimpleDateFormat类,设置参数格式:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd_HH:mm:ss");//输入的格式
java.util.Date date = sdf.parse("2024/1/11_12:12:12");//转换成util.Date类型,要向上抛出异常
看下面干货分享
public static void main(String[] args) throws ParseException {
//类型转换
System.out.println("=======================================");
java.sql.Date sql1 = new java.sql.Date(System.currentTimeMillis());
java.util.Date util1 = sql1;//向上自动转换
System.out.println("自动转换util1:"+util1);
//向下强制转换
sql1 = (java.sql.Date)util1;
System.out.println("强制转换sql1:"+sql1);
//向下构造器转换(不用之前向上转型过)
java.util.Date util3 = new java.util.Date();
java.sql.Date sql2 = new java.sql.Date(util3.getTime());
System.out.println("构造器转换sql2:"+sql2);
//字符串转日期
java.sql.Date sql3 = java.sql.Date.valueOf("2024-1-11");
System.out.println(sql3.getYear());
//时间转换类
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd_HH:mm:ss");//输入的格式
String str = (new Scanner(System.in)).next();
java.util.Date date = sdf.parse(str);//转换成util.Date类型,要向上抛出异常
System.out.println(date);
}
/*
4)【sql.Date转util.Date是向上类型转换】
java.sql.Date sql1 = new java.sql.Date(System.currentTimeMillis());
java.util.Date util1 = sql1;//向上自动转换
【util.Date转sql.Date是向下类型转换】
强制类型转换:(之前向上转型过)
sql1 = (java.sql.Date)util1;//向下强制转换
借助构造器转换:(可没向上转型过)
java.util.Date util3 = new java.util.Date();
java.sql.Date sql2 = new java.sql.Date(util3.getTime());
5)【String转日期】
注意:"2024-1-11"参数格式必须是"年-月-日",否则报错
java.sql.Date sql3 = java.sql.Date.valueOf("2024-1-11");
解决方案:
引入SimpleDateFormat类,设置参数格式:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd_HH:mm:ss");//输入的格式
java.util.Date date = sdf.parse("2024/1/11_12:12:12");//转换成util.Date类型,要向上抛出异常
*/
日期时间类第二阶段(不太重点)
看下面代码了解即可
public static void main(String[] args) {
//获得对象方式一:
Calendar calendar = Calendar.getInstance();//获取当前系统时间
System.out.println(calendar);
//获得对象方式二:
Calendar calendar1 = new GregorianCalendar();//声明父类new子类
System.out.println(calendar1);
//操作方法:
System.out.println(calendar.get(Calendar.YEAR));//年,实际年份
System.out.println(calendar.get(Calendar.MONTH));//月,实际月份-1
System.out.println(calendar.get(Calendar.DATE));//日,实际日期
System.out.println(calendar.get(Calendar.HOUR));//时,实际时数
System.out.println(calendar.get(Calendar.MINUTE));//分,实际分数
System.out.println(calendar.get(Calendar.DAY_OF_WEEK));//一周中的第几天,周是日开始的
System.out.println(calendar.get(Calendar.WEEK_OF_YEAR));//今年的第几周
//求2024年2月11是一年的第几天
calendar.set(2024,1,1);//月份0开始
calendar.set(Calendar.DATE,5);//修改日为5
System.out.println("今年的第几天"+calendar.get(Calendar.DAY_OF_YEAR));//今年的第几天
System.out.println(calendar.getActualMaximum(Calendar.DATE));//获取对象的当前时间月份最大天数
System.out.println("================================================");
//String 转 java.sql.Date();
java.sql.Date sql1 = java.sql.Date.valueOf("2024-12-20");
//sql.Date 转 Calendar
Calendar calendar2 = new GregorianCalendar();
calendar2.setTime(sql1);
System.out.println(calendar2.get(Calendar.DATE));
}
/*
2.第二阶段:
因为第一阶段方法的大部分都过期了,用Calender时间类型代替了
Calendar是一个抽象类,不可以直接创建对象,可Calendar calendar = Calendar.getInstance();创建
GregorianCalendar()子类 extends Calendar(父类是一个抽象类)
*/
Calendar类是一个抽象类,不可以被实例化,可通过下面代码代替实例化 | |
---|---|
Calendar calendar = Calendar.getInstance();//获取当前系统时间 | 常用 |
Calendar calendar = new GregorianCalendar();//声明父类new子类 | 少用 |
calendar.get(Calendar.下面字段值);
字段值 | 含义 |
---|---|
YEAR | 年 |
MONTH | 月份 0-11代表的是1-12月 |
DATE | 月中的某一天(几号) |
DAY_OF_MONTH | 月中的第几天 |
HOUR | 时(12小时制度) |
HOUR_OF_DAY | 时(24小时制度) |
MINUTE | 分 |
SECOND | 秒 |
DAY_OF_WEEK | 一周中的第几天(周几,周日为1) |
public static Calendar getInstance() | 使用默认时区和默认的语言环境获取一个日历对象 |
public int get(int field) | 获取给定的日历字段值 |
public void set(int field,int value) | 将给定的字段设定为给定的值 |
public abstract void add(int field,int amount) | 根据日历规则,将给定的日历字段添加或者减少指定的时间值 |
public Date getTime() | 把日历对象转换成日期对象 |
public long getTimelnMlllis() | 获取日历对象对应的毫秒值 |
sql.Date或util.Date 转 Calendar
Calendar calendar2 = new GregorianCalendar();
calendar2.setTime(Date对象);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");//输入的格式
java.util.Date date = sdf.parse("2024/01/11");//字符串转成util.Date
Calendar calendar2 = new GregorianCalendar();
calendar2.setTime(date);//util.Date转Calendar
//想用SimpleDateFormat格式只能转成util.Date再转成sql.Date
java.sql.Date date = java.sql.Date.valueOf("2024-01-01");//字符串转换成sql.Date类型
Calendar calendar2 = new GregorianCalendar();
calendar2.setTime(date);//sql.Date
扩展:(部分了解即可)
Calendar cal=Calendar.getInstance(); | 获取系统时间,也是使用Calendar的方式 |
---|---|
cal.getTimeInMillis(); | 获取当前时间的毫秒数,可以用来测试程序运行时间` |
cal.setTime(date); | set方法,将日期设置为Calendar日期 |
cal.set(Calendar.YEAR,1990); | 改变日历的年份 |
cal.set(Calendar.MONTH,3); | 改变日历的月份 |
cal.set(Calendar.DATE,16); | 改变日历的日期 |
set方法也可以设置时分秒 | |
Cal.getTime(); | get方法,获取当前时间,可以是系统时间也可以是经过操作后的时间 |
cal.get(Calendar.Year); | get方法,可以用这个方法获得年份 |
cal.get(Calendar.Month); | 获得月份,月份从0开始``正常月份=获得月份+1 |
cal.get(Calendar.DAY_OF_MONTH); | 获得日期在这个月中是第几天 |
cal.get(Calendar.DAY_OF_WEEK) | 获取所在日期是周几``以国外为例,国内要-1 |
cal.getActualMaximum(Calendar.DATE); | 获取所在月日期的最大天数,28,28,30,31 |
cal.getActualMinimum(Calendar.DATE); | 获取所在月日期的最小天数,一般为1 |
get方法还可以获取小时,分钟,秒等 | |
cal.add(Calendar.YEAR,1); | add方法,可以让年数加1,如果为负数则减一年 |
cal.add(Calendar.MONTH,1); | 月份加减 |
cal.add(Calendar.DATE,1); | 日期加减 |
add同样可以用于时分秒 | |
cal.get(Calendar.DAY_OF_YEAR); | 输入的日期是这一年的第多少天 |
cal.get(Calendar.DATE)); | 输入的日期是这个月的第多少天 |
日期1.before(日期2) | 第一个日期在第二个日期之前的返回true |
日期1.after(日期2) | 第一个日期在第二个日期之后的返回true |
日期时间类第三阶段(重点)
解决的问题:
可变性:像日期和时间这样的类应该是不可变的。//有改变应该创建新对象接收他,旧对象不变
偏移性:Date中 的年份是从1900开始的,而月份都从0开始。//月份0开始麻烦
格式化:格式化只对Date有用,Calendar则不行。//不能直接String直接转成Calendar
LocalDate/LocalTime/LocalDateTime(常用)
localDateTime的方法 | 说明 | 用法 |
---|---|---|
LocalDateTime.now(); | 获取当前年-月-日T时:分:秒.毫秒 | LocalDateTime a = LocalDateTime.now(); |
LocalDateTime.of(参数) | 设置年-月-日T时:分:秒 | LocalDateTime a = LocalDateTime.of(1890, 12, 23, 13, 24, 15); |
a.getYear() | 年:2024 | System.out.println(localDateTime.getYear()); |
a.getMonth() | 月:1 | System.out.println(localDateTime.getMonth()); |
a.getMonthValue() | 日:3 | System.out.println(localDateTime.getDayOfMonth()); |
a.getDayOfWeek() | 星期:WEDNESDAY | System.out.println(localDateTime.getDayOfWeek()); |
a.getHour() | 时:19 | System.out.println(localDateTime.getHour()); |
a.getMinute() | 分:16 | System.out.println(localDateTime.getMinute()); |
a.getSecond() | 秒:31 | System.out.println(localDateTime.getSecond()); |
a.withMonth(月份) | 重新设置月份 | LocalDateTime a = localDateTime.withMonth(8); |
a.plusMonths(值) | 月份加法 | LocalDateTime a = localDateTime.plusMonths(4);//+4 |
a.minusMonths(值) | 月份减法 | LocalDateTime a = localDateTime.minusMonths(5); |
看下面干货分享
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.time.temporal.TemporalAccessor;
public class Demo4 {
public static void main(String[] args) {
LocalDate localDate = LocalDate.now();//年-月-日
System.out.println(localDate);//2024-01-11
LocalTime localTime = LocalTime.now();//时:分:秒.毫秒
System.out.println(localTime);//10:22:43.434033500
LocalDateTime localDateTime = LocalDateTime.now();//年-月-日T时:分:秒.毫秒
System.out.println(localDateTime);//2024-01-11T10:22:43.434033500
//解决了一个对象代表一个时间的问题,不会修改旧对象(不可变性)
LocalDate localDate1 = localDate.plusMonths(2);
System.out.println(localTime);//旧对象不改变
//查看月份,不是0开始的了
System.out.println("月份"+localDate.getMonthValue());//1开始
/*转换格式:
1.(一般不用)
2.(一般不用)
3.自定义格式:下面的代码
*/
//日期转字符串
DateTimeFormatter df2 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT);
LocalDateTime now1 = LocalDateTime.now();
String str2 = df2.format(now1);
System.out.println(str2);
//字符串转日期
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
TemporalAccessor parse = dtf.parse("2024-01-11 12:12:12");//字符串转时间,要两位数
System.out.println(parse);
}
/*
3.第三阶段:
可变性:像日期和时间这样的类应该是不可变的。//有改变应该创建新对象接收他,旧对象不变
偏移性:Date中 的年份是从1900开始的,而月份都从0开始。//月份0开始麻烦
格式化:格式化只对Date有用,Calendar则不行。//不能直接String直接转成Calendar
*/
}
字符串类(重点)
字符串的分类:
不可变字符串:String
(是final修饰的)(有改变应该创建新对象接收他,旧对象不变)
可变字符串:StringBuilder,StringBuffer
不可变性:地址不变的情况下,内容是不会变的,会复制一份
常用方法:
String的方法(不改变原数据) | 说明 | 返回值 |
---|---|---|
length() | 获取字符串长度 | int |
isEmpty() | 判断是否为空 | boolean |
charAt(下标) | 获取字符串对应下标的字符(0开始) | char |
toCharArray() | 转成字符数组 | char[] |
equals(字符串) | 判断字符串内容是否相等 | boolean |
compareTo(字符串2) | 两个字符串按字典顺序比较结果(0:相等, 负数:前面的字符串1小于(字符串2),正数:字符串1大于(字符串2)) | int |
substring(开始下标) | 从开始下标(包含自己)开始截取到末尾 | String |
substring(开始下标,结束下标) | 从开始下标(包含自己)截取到结束下标(不包含) | String |
concat(字符串) | 拼接两个字符串(一般用+拼接就行) | String |
replace(字符串1,字符串2) | 字符串1替换成字符串2 | String |
split(字符串,[部分数]) | 字符串切分(特殊:‘\\.’、‘\\*’、‘\\+’…需要转义),可写切分的部分数 最后一个字符一样的话不单独切分出空数组 | String[] |
[last]indexOf(字符串) | 返回原字符串第一次出现该字符串的索引,没有返回-1 | int |
toUpperCase() | 字符串全转成大写 | String |
toLowerCase() | 字符串全转成小写 | String |
trim() | 去除原始字符串两端的空白符(空格、制表符、换行符) | String |
String.valueOf(值) | 转换成字符串类型 | String |
toString() | 直接使用字符串对象即可,无需显式调用 toString 方法 | String |
StringBuilder和StringBuffer的方法 | 说明 | 返回值 |
---|---|---|
length() | 获取字符串长度 | int |
charAt(下标) | 获取下标(0开始)位置的字符 | char |
append(任何类型) | 在原字符串后追加字符串 | void |
delete(开始下标,结束下标) | 删除开始下标(包含)到结束下标(不包含)的字符 | void |
deleteCharAt(下标) | 删除当前下标(0开始)的字符 | void |
insert(下标,任何类型) | 在下标位置插入字符串(不覆盖) | void |
replace(开始下标,结束下标,字符串) | 用字符串替换开始下标(包含)到结束下标(不包含)的字符串 | void |
setCharAt(下标,字符) | 将下标(0开始)位置的字符替换成该字符 | void |
substring(开始下标,结束下标) | 截取开始下标(包含)到结束下标(不包含)内的字符串 对StringBuilder没有影响 | String |
indexOf(字符串,[下标]) | [从下标开始],返回字符串第一次出现的位置,没有返回-1 | int |
reverse() | 反转字符串 | String |
toString() | 返回String类型,可直接使用,无需显式调用 toString 方法 | String |
String、StringBuffer
、StringBuilder区别与联系(重点)
String类是不可变类,即一旦一个String对象被创建后,包含在这个对象中的字符序列是不可改变的,直至这个对象销毁。
StringBuffer类则代表一个字符序列可变的字符串,可以通过append、insert、reverse、setChartAt、setLength等方法改变其内容。一旦生成了最终的字符串,调用toString方法将其转变为String
- JDK1.5新增了一个StringBuilder类,与StringBuffer相似,构造方法和方法基本相同。不同是StringBuffer是线程安全的,而StringBuilder是线程不安全的,所以性能略高。通常情况下,创建一个内容可变的字符串,应该优先考虑使用StringBuilder
StringBuilder:JDK1.5开始 效率高 多线程不安全
(常用,一般常用都是线程不安全且效率高的常用)
StringBuffer:JDK1.0开始 效率低 多线程安全
其他(了解)
直接输出对象,相当于是输出了对象.toString()方法。
System.out.println(对象);==System.out.println(对象.toString());
方法名出现横线说明方法过期了,不推荐使用
自己找:
String s1 = "a";
String s2 = s1 + "b";//编译生成新对象,新对象(新地址)内容为"ab"
String s3 = "a" + "b";//编译直接就是"ab"了,会进行编译器优化,直接合并成为完整的字符串
String s4 = "ab";//编译直接就是"ab"了
System.out.println(s2 == "ab");//==是比地址
System.out.println(s3 == "ab");
System.out.println(s4 == "ab");
//第一条语句打印的结果为false,第二条语句打印的结果为true,第三条语句打印的结果为true
从String作为对象理解下,==比较的是对象的地址,equals比较的才是对象的内容,在变量+字面量会生成新的对象
javac编译可以对字符串常量直接相加的表达式进行优化,不必要等到运行期去进行加法运算处理,而是在编译时去掉其中的加号,直接将其编译成一个这些常量相连的结果
习题:
``3.以下关于StringBuffer类的代码的执行结果是( )。(选择一项)` | D` |
---|---|
A. | A.B |
B. | A.A |
C. | AB.AB |
D. | AB.B |
public class TestStringBuffer {
public static void main(String args[]) {
StringBuffer a = new StringBuffer("A");
StringBuffer b = new StringBuffer("B");
mb_operate(a, b);
System.out.println(a + "." + b);
}
static void mb_operate(StringBuffer x, StringBuffer y) {
x.append(y);
y = x;
}
}
解答:在Java中,参数传递是按值传递的,这意味着在方法调用时,传递给方法的是实际参数的拷贝。对于基本数据类型,这是原始值的拷贝,而对于对象,这是对象引用的拷贝。
在这个方法中,x.append(y) 修改了 x 对象,将 y 的内容追加到了 x 后面。但是 y = x 这一行只是将局部变量 y 的引用指向了 x,并没有影响到原始的 b 对象
。因为这只是修改了局部变量 y 的引用,不会改变原始的 b 对象。
自己整理: | ||
---|---|---|
首先 | a->“A” | b->“B” |
调用mb_operate(a, b)后: | a->“A”,x->“A” | b->“B”,y->“B” |
执行x.append(y)后:改变的是值 | a->“AB”,x->“AB” | b->“B”,y->“B” |
执行y = x;后:改变的是y指向的地址 | a->“AB”,x->“AB” | b->“B”,y->“AB” |
面试题(重点)
java.sql.Date和java.util.Date的联系和区别
java.sql.Date是java.util.Date的子类
,是一个包装了毫秒值的瘦包装器,允许 JDBC 将毫秒值标识为 SQL DATE 值。毫秒值表示自 1970 年 1 月 1 日 00:00:00 GMT以来经过的毫秒数。 为了与SQL DATE 的定义一致,由 java.sql.Date 实例包装的毫秒值必须通过将时间、分钟、秒和毫秒设置为与该实例相关的特定时区中的零来“规范化”。 说白了,java.sql.Date就是与数据库Date相对应的一个类型,而java.util.Date是纯java的Date。
JAVA里提供的日期和时间类,java.sql.Date和java.sql.Time,只会从数据库里读取某部分值,这有时会导致丢失数据。例如一个包含2002/05/22 5:00:57 PM的字段,读取日期时得到的是2002/05/22,而读取时间时得到的是5:00:57 PM. 你需要了解数据库里存储时间的精度。有些数据库,比如MySQL,精度为毫秒,然而另一些数据库,包括Oracle,存储SQL DATE类型数据时,毫秒部分的数据是不保存的。以下操作中容易出现不易被发现的BUG:获得一个JAVA里的日期对象。 从数据库里读取日期 试图比较两个日期对象是否相等。如果毫秒部分丢失,本来认为相等的两个日期对象用Equals方法可能返回false。.sql.Timestamp类比java.util.Date类精确度要高。这个类包了一个getTime()方法,
但是它不会返回额外精度部分的数据,因此必须使用…
总之,java.util.Date 就是Java的日期对象,而java.sql.Date 是针对SQL语句使用的,只包含日期而没有时间部分。
String类为什么是final的
为了效率
。若允许被继承,则其高度的 被使用率可能会降低程序的性能。为了安全
。JDK中提供的好多核心类比如String,这类的类的内部好多方法的实现都不是java编程语言本身编写的,好多方法都是调用的操作系统本地的API,这就是著名的“本地方法调用”,也只有这样才能做事,这种类是非常底层的, 和操作系统交流频繁的
,那么如果这种类可以被继承的话,如果我们再把它的方法重写了,往操作系统内部写入一段具有恶意攻击性质的代码什么的, 这不就成了核心病毒了么?不希望别人改
,这个类就像一个工具一样,类的提供者给我们提供了, 就希望我们直接用就完了,不想让我们随便能改,其实说白了还是安全性, 如果随便能改了,那么java编写的程序肯定就很不稳定,你可以保证自己不乱改, 但是将来一个项目好多人来做,管不了别人,再说有时候万一疏忽了呢?他也不是估计的, 所以这个安全性是很重要的,java和C++相比,优点之一就包括这一点;
String、StringBuffer、StringBuilder区别与联系
- String类是不可变类,即一旦一个String对象被创建后,包含在这个对象中的字符序列是不可改变的,直至这个对象销毁。
- StringBuffer类则代表一个字符序列可变的字符串,可以通过append、insert、reverse、setChartAt、setLength等方法改变其内容。一旦生成了最终的字符串,调用toString方法将其转变为String
- JDK1.5新增了一个StringBuilder类,与StringBuffer相似,构造方法和方法基本相同。不同是StringBuffer是线程安全的,而StringBuilder是线程不安全的,所以性能略高。通常情况下,创建一个内容可变的字符串,应该优先考虑使用StringBuilder
String类型是基本数据类型吗?基本数据类型有哪些
基本数据类型包括byte、int、char、long、float、double、boolean和short。
java.lang.String类是引用数据类型,并且是final类型的,因此不可以继承这个类、不能修改这个类。为了提高效率节省空间,我们应该用StringBuffer类
String s=“Hello”;s=s+“world!”;执行后,s内容是否改变?
没有。因为String被设计成不可变(immutable)类,所以它的所有对象都是不可变对象。在这段代码中,s原先指向一个String对象,内容是 “Hello”,然后我们对s进行了+操作,那么s所指向的那个对象是否发生了改变呢?答案是没有。这时,s不指向原来那个对象了,而指向了另一个 String对象,内容为"Hello world!",原来那个对象还存在于内存之中,只是s这个引用变量不再指向它了。
通过上面的说明,我们很容易导出另一个结论,如果经常对字符串进行各种各样的修改,或者说,不可预见的修改,那么使用String来代表字符串的话会引起很大的内存开销。因为 String对象建立之后不能再改变,所以对于每一个不同的字符串,都需要一个String对象来表示。这时,应该考虑使用StringBuffer类,它允许修改,而不是每个不同的字符串都要生成一个新的对象。并且,这两种类的对象转换十分容易。
同时,我们还可以知道,如果要使用内容相同的字符串,不必每次都new一个String。例如我们要在构造器中对一个名叫s的String引用变量进行初始化,把它设置为初始值,应当这样做:
public class Demo {
private String s;
...
public Demo {
s = "Initial Value";
}
...
}
而非
s = new String("Initial Value");
后者每次都会调用构造器,生成新对象,性能低下且内存开销大,并且没有意义,因为String对象不可改变,所以对于内容相同的字符串,只要一个String对象来表示就可以了。也就说,多次调用上面的构造器创建多个对象,他们的String类型属性s都指向同一个对象。
上面的结论还基于这样一个事实:对于字符串常量,如果内容相同,Java认为它们代表同一个String对象。而用关键字new调用构造器,总是会创建一个新的对象,无论内容是否相同。
至于为什么要把String类设计成不可变类,是它的用途决定的。其实不只String,很多Java标准类库中的类都是不可变的。在开发一个系统的时候,我们有时候也需要设计不可变类,来传递一组相关的值,这也是面向对象思想的体现。不可变类有一些优点,比如因为它的对象是只读的,所以多线程并发访问也不会有任何问题。当然也有一些缺点,比如每个不同的状态都要一个对象来代表,可能会造成性能上的问题。所以Java标准类库还提供了一个可变版本,即 StringBuffer
。
String s = new String(“xyz”);创建几个String Object?
两个或一个,”xyz”对应一个对象,这个对象放在字符串常量缓冲区,常量”xyz”不管出现多少遍,都是缓冲区中的那一个。New String每写一遍,就创建一个新的对象,它一句那个常量”xyz”对象的内容来创建出一个新String对象。如果以前就用过’xyz’,这句代表就不会创建”xyz”自己了,直接从缓冲区拿。
下面这条语句一共创建了多少个对象:String s=“a”+“b”+“c”+“d”;
答:对于如下代码:
String s1 = “a”;
String s2 = s1 + “b”;
String s3 = “a” + “b”;
System.out.println(s2 == “ab”);
System.out.println(s3 == “ab”);
第一条语句打印的结果为false,第二条语句打印的结果为true,这说明javac编译可以对字符串常量直接相加的表达式进行优化,不必要等到运行期去进行加法运算处理,而是在编译时去掉其中的加号,直接将其编译成一个这些常量相连的结果。
题目中的第一行代码被编译器在编译时优化后
,相当于直接定义了一个”abcd”的字符串,所以,上面的代码应该只创建了一个String对象。写如下两行代码,
String s = "a" + "b" + "c" + "d";
System.out.println(s == "abcd");
最终打印的结果应该为true。