重温JavaSE13、14

日期时间类第一阶段(偏重点)

java.util.Date

里面很多过期的方法不推荐使用

java.util.Date可以精确到 年-月-日 时:分:秒

Date d = new Date()创建对象后的方法输出
d.toString();//直接输出d也一样星期 月份 日子 时:分:秒 中国标准地区 年
Wed Jan 03 12:39:23 CST 2024
toGMTString();//过期方法3 Jan 2024 04:39:23 GMT
d.toLocaleString();2024年1月3日 下午12:39:23
1900+d.getYear();//1900+1242024
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
  1. 向上类型转换(自动)

java.sql.Date sql1 = new java.sql.Date(System.currentTimeMillis());//系统时间
java.util.Date util1 = sql1;//向上自动转换

util.Date转sql.Date
  1. 向下类型转换(强制类型转换:(需之前向上转型过))

    sql1 = (java.sql.Date)util1;//向下强制转换

  2. 借助构造器转换:(可没向上转型过)

​ 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()年:2024System.out.println(localDateTime.getYear());
a.getMonth()月:1System.out.println(localDateTime.getMonth());
a.getMonthValue()日:3System.out.println(localDateTime.getDayOfMonth());
a.getDayOfWeek()星期:WEDNESDAYSystem.out.println(localDateTime.getDayOfWeek());
a.getHour()时:19System.out.println(localDateTime.getHour());
a.getMinute()分:16System.out.println(localDateTime.getMinute());
a.getSecond()秒:31System.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替换成字符串2String
split(字符串,[部分数])字符串切分(特殊:‘\\.’、‘\\*’、‘\\+’…需要转义),可写切分的部分数
最后一个字符一样的话不单独切分出空数组
String[]
[last]indexOf(字符串)返回原字符串第一次出现该字符串的索引,没有返回-1int
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(字符串,[下标])[从下标开始],返回字符串第一次出现的位置,没有返回-1int
reverse()反转字符串String
toString()返回String类型,可直接使用,无需显式调用 toString 方法String

String、StringBuffer、StringBuilder区别与联系(重点)

  1. String类是不可变类,即一旦一个String对象被创建后,包含在这个对象中的字符序列是不可改变的,直至这个对象销毁。
  2. StringBuffer类则代表一个字符序列可变的字符串,可以通过append、insert、reverse、setChartAt、setLength等方法改变其内容。一旦生成了最终的字符串,调用toString方法将其转变为String
  3. 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的
  1. 为了效率。若允许被继承,则其高度的 被使用率可能会降低程序的性能。
  2. 为了安全。JDK中提供的好多核心类比如String,这类的类的内部好多方法的实现都不是java编程语言本身编写的,好多方法都是调用的操作系统本地的API,这就是著名的“本地方法调用”,也只有这样才能做事,这种类是非常底层的, 和操作系统交流频繁的,那么如果这种类可以被继承的话,如果我们再把它的方法重写了,往操作系统内部写入一段具有恶意攻击性质的代码什么的, 这不就成了核心病毒了么?
  3. 不希望别人改,这个类就像一个工具一样,类的提供者给我们提供了, 就希望我们直接用就完了,不想让我们随便能改,其实说白了还是安全性, 如果随便能改了,那么java编写的程序肯定就很不稳定,你可以保证自己不乱改, 但是将来一个项目好多人来做,管不了别人,再说有时候万一疏忽了呢?他也不是估计的, 所以这个安全性是很重要的,java和C++相比,优点之一就包括这一点;
String、StringBuffer、StringBuilder区别与联系
  1. String类是不可变类,即一旦一个String对象被创建后,包含在这个对象中的字符序列是不可改变的,直至这个对象销毁。
  2. StringBuffer类则代表一个字符序列可变的字符串,可以通过append、insert、reverse、setChartAt、setLength等方法改变其内容。一旦生成了最终的字符串,调用toString方法将其转变为String
  3. 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。

  • 22
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值