一、String 、StringBuilder、StringBuffer的区别?

二、什么情况下用“+”运算符进行字符串连接比调用StrigBuffer/StringBuilder的append方法连接字符串性能更好?

三、请说出下面程序的输出?

class StringEqualTest {
    public static void main(String[] args) {
        String s1 = "Programming";
        String s2 = new String("Programming");
        String s3 = "Program";
        String s4 = "ming";
        String s5 = "Program" + "ming";
        String s6 = s3 + s4;
        System.out.println(s1 == s2);
        System.out.println(s1 == s5);
        System.out.println(s1 == s6);
        System.out.println(s1 == s6.intern());
        System.out.println(s2 == s2.intern());
    }
}

四、如何获得年月日、小时、分钟、秒?

五、如何获取从1970年1月1日0时0分0秒到现在的毫秒数

六、如何获取某月的最后一天?

七、如何格式化日期?

八、如何打印昨天的当前时刻?

九、请说说java8时间/日期有什么特性?

十、JSR310 规范 Joda-Time 的区别

最后总结

--------------------------------答案分界线------------------------------------------

一、Java 平台提供了两种类型的字符串:String 和 StringBuffer/StringBuilder,它们都可以储存和操作字符串,区别如下。

1)String 是只读字符串,也就意味着 String 引用的字符串内容是不能被改变的。初学者可能会有这样的误解:

String str = “abc”;
str = “bcd”;

如上,字符串 str 明明是可以改变的呀!其实不然,str 仅仅是一个引用对象,它指向一个字符串对象“abc”。第二行代码的含义是让 str 重新指向了一个新的字符串“bcd”对象,而“abc”对象并没有任何改变,只不过该对象已经成为一个不可及对象罢了。

2)StringBuffer/StringBuilder 表示的字符串对象可以直接进行修改。
3)StringBuilder 是 Java5 中引入的,它和 StringBuffer 的方法完全相同,区别在于它是在单线程环境下使用的,因为它的所有方法都没有被 synchronized 修饰,因此它的效率理论上也比 StringBuffer 要高。

 

二、该题来自华为。
字符串是 Java 程序中最常用的数据结构之一。在 Java 中 String 类已经重载了"+"。也就是说,字符串可以直接使用"+"进行连接,如下面代码所示:

String s = "abc" + "ddd";

但这样做真的好吗?当然,这个问题不能简单地回答 yes or no。要根据具体情况来定。在 Java 中提供了一个StringBuilder 类(这个类只在 J2SE5 及以上版本提供,以前的版本使用 StringBuffer 类),这个类也可以起到"+"的作用。那么我们应该用哪个呢?
下面让我们先看看如下的代码:

package string;

public class TestSimplePlus
{
    public static void main(String[] args)
    {
        String s = "abc";
        String ss = "ok" + s + "xyz" + 5;
        System.out.println(ss);
    }
}

我们首先使用反编译工具(如 jdk 带的 javap、或 jad)将 TestSimplePlus 反编译成 Java Byte Code,其中的奥秘就一目了然了。在本文将使用 jad 来反编译,命令如下:
        jad -o -a -s d.java TestSimplePlus.class
反编译后的代码如下

package string;
import java.io.PrintStream;
public class TestSimplePlus
{
    public TestSimplePlus()
    {
// 0 0:aload_0
// 1 1:invokespecial #8 <Method void Object()>
// 2 4:return
    }

    public static void main(String args[])
    {
        String s = "abc";
// 0 0:ldc1 #16 <String "abc">
// 1 2:astore_1
        String ss = (new StringBuilder("ok")).append(s).append("xyz").append(5).toString();
// 2 3:new #18 <Class StringBuilder>
// 3 6:dup
// 4 7:ldc1 #20 <String "ok">
// 5 9:invokespecial #22 <Method void StringBuilder(String)>
// 6 12:aload_1
// 7 13:invokevirtual #25 <Method StringBuilder StringBuilder.append(String)>
// 8 16:ldc1 #29 <String "xyz">
// 9 18:invokevirtual #25 <Method StringBuilder StringBuilder.append(String)>
// 10 21:iconst_5
// 11 22:invokevirtual #31 <Method StringBuilder StringBuilder.append(int)>
// 12 25:invokevirtual #34 <Method String StringBuilder.toString()>
// 13 28:astore_2
        System.out.println(ss);
// 14 29:getstatic #38 <Field PrintStream System.out>
// 15 32:aload_2
// 16 33:invokevirtual #44 <Method void PrintStream.println(String)>
// 17 36:return
    }
}

读者可能看到上面的 Java 字节码感到迷糊,不过大家不必担心。本文的目的并不是讲解 Java Byte Code,因此,并不用了解具体的字节码的含义。


使用 jad 反编译的好处之一就是可以同时生成字节码和源代码。这样可以进行对照研究。从上面的代码很容易看出,虽然在源程序中使用了"+",但在编译时仍然将"+"转换成 StringBuilder。因此,我们可以得出结论,在 在 Java 中 中无论使用何种方式进行字符串连接,实际上都使用的是 无论使用何种方式进行字符串连接,实际上都使用的是 StringBuilder。 


那么是不是可以根据这个结论推出使用"+"和 StringBuilder 的效果是一样的呢?这个要从两个方面的解释。如果从运行结果来解释,那么"+"和 StringBuilder 是完全等效的。但如果从运行效率和资源消耗方面看,那它们将存在很大的区别。


当然,如果连接字符串行表达式很简单(如上面的顺序结构),那么"+"和 StringBuilder 基本是一样的,但如果结构比较复杂,如使用循环来连接字符串,那么产生的 Java Byte Code 就会有很大的区别。在使用 StringBuilder 时要注意,尽量不要"+"和 StringBuilder 混着用,否则会创建更多的 StringBuilder 对象

上面的代码在 JDK1.4 下编译,必须将 StringBuilder 改为 StringBuffer,而 JDK1.4 将"+"转换为StringBuffer(因为 JDK1.4 并没有提供 StringBuilder 类)。StringBuffer 和 StringBuilder 的功能基本一样,只是StringBuffer 是线程安全的,而 StringBuilder 不是线程安全的。因此,StringBuilder 的效率会更高。

 

三、System.out.println(s1 == s2); //false
System.out.println(s1 == s5); //true
System.out.println(s1 == s6); //false
System.out.println(s1 == s6.intern()); //true
System.out.println(s2 == s2.intern()); //false

补充:解答上面的面试题需要知道如下两个知识点:
1. String 对象的 intern()方法会得到字符串对象在常量池中对应的版本的引用(如果常量池中有一个字符串与String 对象的 equals 结果是 true),如果常量池中没有对应的字符串,则该字符串将被添加到常量池中,然后返回常量池中字符串的引用;
2. 字符串的+操作其本质是创建了 StringBuilder 对象进行 append 操作,然后将拼接后的 StringBuilder 对象用 toString 方法处理成 String 对象,这一点可以用 javap -c StringEqualTest.class 命令获得 class 文件对应的 JVM 字节码指令就可以看出来。

 

四、java获取时间

public class DateTimeTest {
    public static void main(String[] args) {
        Calendar cal = Calendar.getInstance();
        System.out.println(cal.get(Calendar.YEAR));
        System.out.println(cal.get(Calendar.MONTH)); // 0 - 11
        System.out.println(cal.get(Calendar.DATE));
        System.out.println(cal.get(Calendar.HOUR_OF_DAY));
        System.out.println(cal.get(Calendar.MINUTE));
        System.out.println(cal.get(Calendar.SECOND));
        // Java 8
        LocalDateTime dt = LocalDateTime.now();
        System.out.println(dt.getYear());
        System.out.println(dt.getMonthValue()); // 1 - 12
        System.out.println(dt.getDayOfMonth());
        System.out.println(dt.getHour());
        System.out.println(dt.getMinute());
        System.out.println(dt.getSecond());
    }
}

 

五、1970年到现在的毫秒数

Calendar.getInstance().getTimeInMillis(); //第一种方式
System.currentTimeMillis(); //第二种方式
// Java 8
Clock.systemDefaultZone().millis();

 

六、  如何取得某月的最后一天 

//获取当前月第一天:
Calendar c = Calendar.getInstance();
c.add(Calendar.MONTH, 0);
c.set(Calendar.DAY_OF_MONTH,1);//设置为 1 号,当前日期既为本月第一天
String first = format.format(c.getTime());
System.out.println("===============first:"+first);

//获取当前月最后一天
Calendar ca = Calendar.getInstance();
ca.set(Calendar.DAY_OF_MONTH, ca.getActualMaximum(Calendar.DAY_OF_MONTH));String last = format.format(ca.getTime());
System.out.println("===============last:"+last);

//Java 8
LocalDate today = LocalDate.now();
//本月的第一天
LocalDate firstday = LocalDate.of(today.getYear(),today.getMonth(),1);
//本月的最后一天
LocalDate lastDay =today.with(TemporalAdjusters.lastDayOfMonth());
System.out.println("本月的第一天"+firstday);
System.out.println("本月的最后一天"+lastDay);

 

七、格式化日期

1)Java.text.DataFormat 的子类(如 SimpleDateFormat 类)中的 format(Date)方法可将日期格式化。
2)Java 8 中可以用 java.time.format.DateTimeFormatter 来格式化时间日期,代码如下所示:

import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Date;
class DateFormatTest {

    public static void main(String[] args) {
        SimpleDateFormat oldFormatter = new SimpleDateFormat("yyyy/MM/dd");
        Date date1 = new Date();
        System.out.println(oldFormatter.format(date1));

        // Java 8
        DateTimeFormatter newFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
        LocalDate date2 = LocalDate.now();
        System.out.println(date2.format(newFormatter));
    }
}

补充:Java 的时间日期 API 一直以来都是被诟病的东西,为了解决这一问题,Java 8 中引入了新的时间日期 API,其中包括 LocalDate、LocalTime、LocalDateTime、Clock、Instant 等类,这些的类的设计都使用了不变模式,因此是线程安全的设计。

八、昨天的当前时刻


import java.util.Calendar;
class YesterdayCurrent {
    public static void main(String[] args){
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.DATE, -1);
        System.out.println(cal.getTime());
    }
}

//java-8
import java.time.LocalDateTime;
class YesterdayCurrent {
    public static void main(String[] args) {
        LocalDateTime today = LocalDateTime.now();
        LocalDateTime yesterday = today.minusDays(1);
        System.out.println(yesterday);
    }
}

九、java8时间日期特性

Java 8 日期/时间 API 是 JSR-310 的实现,它的实现目标是克服旧的日期时间实现中所有的缺陷,新的日期/时间API 的一些设计原则是:
1)不变性:新的日期/时间 API 中,所有的类都是不可变的,这对多线程环境有好处。
2) 关注点分离:新的 API 将人可读的日期时间和机器时间(unix timestamp)明确分离,它为日期(Date)、时间(Time)、日期时间(DateTime)、时间戳(unix timestamp)以及时区定义了不同的类。
3) 清晰:在所有的类中,方法都被明确定义用以完成相同的行为。举个例子,要拿到当前实例我们可以使用 now()方法,在所有的类中都定义了 format()和 parse()方法,而不是像以前那样专门有一个独立的类。为了更好的处理问题,所有的类都使用了工厂模式和策略模式,一旦你使用了其中某个类的方法,与其他类协同工作并不困难。
4)  实用操作:所有新的日期/时间 API 类都实现了一系列方法用以完成通用的任务,如:加、减、格式化、解析、从日期/时间中提取单独部分,等等。
5) 可扩展性:新的日期/时间 API 是工作在 ISO-8601 日历系统上的,但我们也可以将其应用在非 ISO 的日历上。

十、JSR310 规范 Joda-Time 的区别

其实 JSR310 的规范领导者 Stephen Colebourne,同时也是 Joda-Time 的创建者,JSR310 是在 Joda-Time 的基础上建立的,参考了绝大部分的 API,但并不是说 JSR310=JODA-Time,下面几个比较明显的区别是:

1. 最明显的变化就是包名(从 org.joda.time 以及 java.time)
2. JSR310 不接受 NULL 值,Joda-Time 视 NULL 值为 0
3. JSR310 的计算机相关的时间(Instant)和与人类相关的时间(DateTime)之间的差别变得更明显
4. JSR310 所有抛出的异常都是 DateTimeException 的子类。虽然 DateTimeException 是一个RuntimeException

最后修改于 2019-10-07 15:33:23
如果觉得我的文章对你有用,请随意赞赏
扫一扫支付
上一篇