搞定Java基础面试题

一.Java面向对象

1.面向对象或者oop都有哪些特性以及你对这些特性的理解?

封装、继承、多态、或者抽象
1、封装:封装就是将数据和操作数据的方法绑定起来,对数据的访问只能通过已定的接口例如get、set方法。面向对象的本质是什么?就是将现实世界描绘成一系列的自治、封闭的对象。我们在类中编写的方法就是对实现细节的封装;我们编写类;就是对数据和数据操作的封装。封装就是隐藏一切的东西,只向外界提高简单的编程接口。
2.继承:继承是从已有的类继承信息创建新类的过程。提供继承信息的类我们称为父类(超类、或者基类);得到继承信息的类被称为子类(派生类)。继承让变化中的软件系统得到一定的延续性,同时也是可变因素的手段。继承只能继承父类的公有方法和数据、私有的除外。
3.多态性:多态性指允许不同子类型的对象对同一消息做出不同的响应。简单的来说就是用同样的对象引用调用同样的方法但是做了不同的事情,多态分为编译时多态性和运行时多态性。方法重载overload实现的时编译时多态也称为前绑定,而方法重写override实现运行时多态也称后绑定。实现运行时多态的条件是:1.方法重载(子类继承父类并重写父类的已有的方法或者抽象方法)2.对象造型(用父类引用子类类型对象、这样同样的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为)
4.抽象:抽象就是将一类对象的共同特征总结出来构造一个类的过程,包括数抽象和行为抽象。抽象只关注对象有哪些属性和行为、并不关注这些行为的细节是什么。

2.访问权限修饰符有哪些以及区别?

public、prote、默认default、private
区别主要是引用或者访问时的范围、依次从大到小
当前类、同包、子类、子包。

3.为什么要用clone?

3.1clone的理解

这时java创建一个对象的方法之一。当一个对象A已经包含有效值、此时需要对一个A和这个对象完全相同的对象B,并且这个对象B的任何操作都不会影响到这个对象A时、我们就需要用clone()这个方法

3.2new一个对象和clone一个对象的过程区别在哪?

new一个对象本意是分配内存。程序执行到new操作符时,会根据new后面的操作符的类型、堆内存中分配相应的空间、然构造函数、填充对象的域、这一步叫做对象的初始化、构造方法返回后,一个对象创建完毕、此时可以把他的引用地址发布到外部,存储在栈中、在外部去引用操作这个对象。
clone的第异步也是分配内存、调用clone方法、分配的内存和原对象相同、然后再使用原对象中对应的各个域、填充新对象的域、填充完之后、clone方法返回、一个新的相同对象被创建,同样可以把这个新对象的引用发布到外面

3.3clone对象的使用

3.3.1 复制对象和复制引用的区别

  1. Person p = new Person(23, "zhang");
  2. Person p1 = p;
  3. System.out.println(p);
  4. System.out.println(p1);
    当 Person p1 = p;执行之后, 是创建了一个新的对象吗? 首先看打印结果:
    1.com.itheima.Person@2f9ee1ac
    2.com.itheima.Person@2f9ee1ac
    可以看出,打印的地址值是相同的,既然地址都是相同的,那么肯定是同一个对象。p 和 p1 只是引用而已,他们
    都指向了一个相同的对象 Person(23, “zhang”) 。 可以把这种现象叫做引用的复制。上面代码执行完成之后, 内
    存中的情景如下图所示
    image
    而下面的代码是真真正正的克隆了一个对象。
1.Person p = new Person(23, "zhang")
2.Person p1 = (Person) p.clone();
3System.out.println(p);
4System.out.println(p1);

从打印结果可以看出,两个对象的地址是不同的,也
就是说创建了新的对象, 而不是把原对象的地址赋给了一个
新的引用变量:

1. com.itheima.Person@2f9ee1ac
2. com.itheima.Person@67f1fba0

image
以上代码执行完成后, 内存中的情景如下图所示:
3.3.2 深拷贝和浅拷贝
上面的示例代码中,Person 中有两个成员变量,分别是 name 和 age, name 是 String 类型, age 是 int 类
型。代码非常简单,如下所示:

public class Person implements Cloneable{
2.privatint age ;
3private String name;
4public Person(int age, String name) {
5this.age = age;
6this.name = name;
7. } 8public Person() {}
9public int getAge() {
10return age;
11. }
12public String getName() {
return name;
14. }
15. @Override
16protected Object clone() throws CloneNotSupportedException {
17return (Person)super.clone();
18. }
19.}

由于 age 是基本数据类型,那么对它的拷贝没有什么疑议,直接将一个 4 字节的整数值拷贝过来就行。但是 name
是 String 类型的, 它只是一个引用, 指向一个真正的 String 对象,那么对它的拷贝有两种方式: 直接将原对象中
的 name 的引用值拷贝给新对象的 name 字段, 或者是根据原 Person 对象中的 name 指向的字符串对象创建一个
新的相同的字符串对象,将这个新字符串对象的引用赋给新拷贝的 Person 对象的 name 字段。这两种拷贝方式分别
叫做浅拷贝和深拷贝。深拷贝和浅拷贝的原理如下图所示:
image
下面通过代码进行验证。如果两个 Person 对象的 name 的地址值相同, 说明两个对象的 name 都指向同一个
String 对象,也就是浅拷贝, 而如果两个对象的 name 的地址值不同, 那么就说明指向不同的 String 对象, 也就
是在拷贝 Person 对象的时候, 同时拷贝了 name 引用的 String 对象, 也就是深拷贝。验证代码如下:

1. Person p = new Person(23, "zhang");
2. Person p1 = (Person) p.clone();
3. String result = p.getName() == p1.getName()
4. ? "clone 是浅拷贝的" : "clone 是深拷贝的";
5. System.out.println(result);
打印结果为:
6. clone 是浅拷贝的

所以,clone 方法执行的是浅拷贝, 在编写程序时要注意这个细节
如何进行深拷贝:
由上一节的内容可以得出如下结论:如果想要深拷贝一个对象,这个对象必须要实现 Cloneable 接口,实现 clone
方法,并且在 clone 方法内部,把该对象引用的其他对象也要 clone 一份,这就要求这个被引用的对象必须也要实现
Cloneable 接口并且实现 clone 方法。那么,按照上面的结论,实现以下代码 Body 类组合了 Head 类,要想深拷贝
Body 类,必须在 Body 类的 clone 方法中将 Head 类也要拷贝一份。代码如下:

1static class Body implements Cloneable{
2public Head head;
3public Body() {}
4public Body(Head head) {this.head = head;}
5@Override
6protected Object clone() throws CloneNotSupportedException {
7Body newBody = (Body) super.clone();
8. newBody.head = (Head) head.clone();
9return newBody;
10. }
11.}
12static class Head implements Cloneable{
13public Face face;
14public Head() {}
15@Override
16protected Object clone() throws CloneNotSupportedException {
17return super.clone();
18. } }
19public static void main(String[] args) throws CloneNotSupportedException {
20Body body = new Body(new Head(new Face()));
21Body body1 = (Body) body.clone();
22. System.out.println("body == body1 : " + (body == body1) );
23. System.out.println("body.head == body1.head : " + (body.head == body1.head));
24.}
打印结果为:
1. body == body1 : false
2. body.head == body1.head : false

二、java语法

1.java有没有goto语句?

goto是Java中保留字、在目前版本的Java中没有使用。根据Java之父编写的《The Java Programming Luanguage》这一本书的附录中给出了一个Java关键字列表、其中有goto和const,但是这两个目前是无法使用的关键字。因此有些地方将它们称为关键字、其实保留字这个次应该有更广泛的意义、因为熟悉C的程序员指导、在系统类库中使用过的有特殊意义的单词或者单词的组合都视为保留字。

2.&和&&的区别?同|和||?

&是运算符有两种用法:(1)按位与;(2)逻辑与
&&运算符是短路与运算。逻辑与跟短路与的差别是非常巨大的,虽然二者都要求运算符左右两端的布尔值都是true整个表达式的值才是true。
&&被称为短路与的原因是、如果&&左边的表达式的值是false时、右边的表达式会被短路掉、不会执行运算。很多时候我们需要的是&&而不是&、原因是例如在验证用户登录时用户名不是null而且不是空字符串,应当写为usename!=null&&!username.equals(""),二者的顺序不能交换、更不能用&运算符、因为第一个条件如果不成立、跟泵不能进行equals比较、否则会产生NullpointerException异常(空指针异常).|和||同是这样

3.在Java中、如何跳出当前多重循环

Java中支持带标签的break和continue语句、作用优点类似于C和C++中的goto语句、但是就像被避免使用goto一样、应该避免使用带标签的break和continue、因为它不会让你的程序变得优雅、反而很多时候相反作用。则我们在最外层循环加入一个标记然后用breakA;跳出多次循环;

3.1 Java中关键字continue、break和return的区别:

continue:跳出本次循环继续下一次循环 其后面语句不执行
break: 跳出循环体,继续执行循环外的函数体
return: 跳出整个函数体,函数体后面的部分不再执行

4.两个对象值相同(x.equals(y) ==true)、但是可有不同的HashCode值,这句话对不对?

不对、如何两个对象x和y满足x.equals(y)==true它们的哈希码(hashCode)必须相同。
Java对于eqauls方法和hashCode方法是这样规定的:(1)如果两个对象相同即equals方法返回true、那么它们的hashCode值一定要相同;(2)如果两个对象的HashCode相同、它们并不一定相同。
当然、你未必按照要求去做、但是如果你违背了上述原则就会发现在使用容器时、相同的对象可以出现在Set集合中、同时新增元素的效率大大降低(对于使用哈希存储的系统、如果哈希码频繁的冲突将会造成存取性能急剧下降)。
首先equals方法及其重写必须满足自反性(x.equals(x)必须是true)、对称性(x.equals(y)返回true,且y.equals(x)同样必须是true)、传递性(x.equals(y)和(y.equals(z))都返回true时、x.equals(z)也必须返回true。一致性(当x和y的引用对象信息没有变时、多次调用x.equals(y)应该得到相同的值)。
实现高质量的equals的方法

实现高质量的 equals 方法的诀窍包括:1. 使用==操作符检查"参数是否为这个
对象的引用";
2. 使用 instanceof 操作符检查"参数是否为正确的类型";
3. 对于类中的关键属性,检查参数传入对象
的属性是否与之相匹配;
4. 编写完 equals 方法后,问自己它是否满足对称性、传递性、一致性;
5. 重写 equals 时
总是要重写 hashCode;
6. 不要将 equals 方法参数中的 Object 对象替换为其他的类型,在重写时不要忘掉@Override 注解

5.是否可以继承String?

String类是final类,不可以被继承。
继承String类本身是一个错误行为,对于String类最好的重用方式是关联关系(Has-A)和依赖关系(Use-A)而不是继承关系(IS-A).

6.当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?

是值传递。Java 语言的方法调用(无论是引用类型还是基本类型)只支持参数的值传递。(共享引用)

1.如果是对基本数据类型的数据进行操作,由于原始内容和副本都是存储实际值,并且是在不同的栈帧内,因此形参的操作,不影响原始内容
2.如果是对引用类型的数据进行操作,分两种情况,一种是形参和实参保持指向同一个对象地址,则形参的操作,会影响实参指向的对象的内容。一种是形参被改动指向新的对象地址(如重新赋值引用),则形参的操作,不会影响实参指向的对象的内容
3.(当一个对象实例作为一个参数被传递到方法中时,参数的
值就是对该对象的引用。对象的属性可以在被调用过程中被改变,但对对象引用的改变是不会影响到调用者的。C++
和 C#中可以通过传引用或传输出参数来改变传入的参数的值。说明:Java 中没有传引用实在是非常的不方便,这一
点在 Java 8 中仍然没有得到改进,正是如此在 Java 编写的代码中才会出现大量的 Wrapper 类(将需要通过方法
调用修改的引用置于一个 Wrapper 类中,再将 Wrapper 对象传入方法),这样的做法只会让代码变得臃肿,尤其
是让从 C 和 C++转型为 Java 程序员的开发者无法容忍。)

7.重载(overload)和重写(override)的区别?重载的方法能否根据返回类型进行区分?
方法的重载和重写都是实现多态的方式。区别是前者是编译时多态、后者是实现运行时多态。重载发生在一个类中、同名的方法如果不同的参数列表(参数类型、参数个数或者两者都不相同)则视为重载:重写发生在子类和父类之间,重写方法与父类被重写有相同的返回类型、比父类被重写的方法更容易访问、不能比父类被重写方法声明更多的异常(里氏替代原则)。重载对放回类型没有特殊的要求
方法重载的原则:
1.方法名一致、参数列表中参数的顺序、类型、个数不同
2.重载与方法返回值无关、存在父类和子之类、同类中
3.可以抛出不同的异常、可以有不同的修饰符
方法重写的原则:
1.参数列表必须完全与被重写的方法一致、放回类型也必须一致
2.构造方法不能被重写、声明final的方法不能被重写、声明为static的方法不能被重写、但是能够被再次声明。
3.访问权限不能比父类的低
4.重写方法能够抛出任何非强制性异常(UncheckedException,也叫非运行时异常),无论被重写的方法是否抛弃异常。但是、重写的方法不能抛出新的强制性异常、或者比被重写方法声明的更广泛的强制性异常,反之则可以。

8 为什么函数不能根据返回类型区分重载

因为调用时不能指定类型信息,编译器不知道你要调用哪个函数。
例如:

float max(int a, int b);
int max(int a, int b);

当调用 max(1, 2);时无法确定调用的是哪个,单从这一点上来说,仅返回值类型不同的重载是不应该允许的。
再比如对下面这两个方法来说,虽然它们有同样的名字和自变量,但其实是很容易区分的:

void f(){}
int f(){}

若编译器可根据上下文(语境)明确判断出含义,比如在 int x=f()中,那么这样做完全没有问题。然而,
我们也可能调用一个方法,同时忽略返回值;我们通常把这称为“为它的副作用去调用一个方法”,因为我
们关心的不是返回值,而是方法调用的其他效果。所以假如我们像下面这样调用方法: f(); Java 怎样判断 f()的具体调用方式呢?而且别人如何识别并理解代码呢?由于存在这一类的问题,所以不能。
函数的返回值只是作为函数运行之后的一个“状态”,他是保持方法的调用者与被调用者进行通信的关键。并不能作为某个方法的“标识"

9. char 型变量中能不能存储一个中文汉字,为什么?

char类型可以存储一个中文汉字、因为Java中使用的编码是Unicode(不选则任何特定的编码、之间使用字符再字符集中的编码、这是统一的唯一的方法),一个char类型占2个字节(16比特)、所以放一个中文是没问题的。
补充:使用Unicode意味着字符再JVM内部和外部有着不同的表现形式、在JVM内部都是Unicod、当这个字符被JVM内部转移到外部时(例如存入文件系统中)、需要进行编码转换。所以Java有字节流和字符流、以及在字符流和字节之间的转换流,如InputSteamReader和OutputStreamReader,当这两类是字节流和字符流之间的适配器类、承担编码转换的任务。C完成编码和这类转换依赖于union(联合体/共同体)共享捏村的特征完成

10. 抽象类(abstract class)和接口(interface)有什么异同?

不同
抽象类:
1.抽象类可以定义构造器
2.可以有抽象方法和具体方法
3.接口中的成员都是public的
4.抽象类中可以定义成员变量
5.有抽象方法的类必须声明为抽象类、而抽象类未必要有抽象方法
6.抽象类中可以包含静态方法
7.一个类只能继承一个抽象类
接口:
1.接口中不能有构造器
2.方法全部是抽象方法
3.抽象类中的成员可以是任意权限修饰符
4.接口中定义的成员变量实际上都是常量
5.接口中不能有静态方法
6.一个类可以实现多个接口
相同:
1.不能够实例化
2.可以将抽象类和接口类型作为引用类型
3.一个类如果继承了某个抽象类或者实现了某 个接口都需要对其中的抽象方法全部进行实现,否则该类仍然需要被声明为抽象类
抽象类是对抽象的行为特征的总结现实中可见、接口是偏向不可见。

11. 抽象的(abstract)方法是否可同时是静态的(static), 是否可同时是本地方法(native),是否可同时被 synchronized

都不能。抽象方法需要子类重写,而静态的方法是无法被重写的,因此二者是矛盾的。本地方法是由
本地代码(如 C 代码)实现的方法,而抽象方法是没有实现的,也是矛盾的。synchronized 和方法的实现细节有关,抽象方法不涉及实现细节,因此也是相互矛盾的。

12. 阐述静态变量和实例变量的区别?

静态变量: 是被 static 修饰符修饰的变量,也称为类变量,它属于类,不属于类的任何一个对象,一个类不管创建多少个对象,静态变量在内存中有且仅有一个拷贝,仅仅被加载一次;
实例变量: 必须依存于某一实例,需要先创建对象然后通过对象才能访问到它。静态变量可以实现让多个对象共享内存。

13. ==和 equals 的区别?

equals 和== 最大的区别是一个是方法一个是运算符。
==:如果比较的对象是基本数据类型,则比较的是数值是否相等;如果比较的是引用数据类型,则比较的是对象的地址值是否相等
equals():用来比较方法两个对象的内容是否相等。
注意:equals 方法不能用于基本数据类型的变量,如果(obj)没有对 equals 方法进行重写,则比较的是引用类型的变
量所指向的对象的地址。

14.break 和 continue 的区别?

break 和 continue 都是用来控制循环的语句。
break 用于完全结束一个循环,跳出循环体执行循环后面的语句。
continue 用于跳过本次循环,执行下次循环

15. String s = "Hello";s = s + " world!";这两行代码执行后,原始的 String 对象中的内容到底变了没有?

没有。因为 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;
...
s = "Initial Value";
...
}
而非
s = new String("Initial Value");

后者每次都会调用构造器,生成新对象,性能低下且内存开销大,并且没有意义,因为 String 对象不可改变,所
以对于内容相同的字符串,只要一个 String 对象来表示就可以了。也就说,多次调用上面的构造器创建多个对象,他们的 String 类型属性 s 都指向同一个对象。
上面的结论还基于这样一个事实:对于字符串常量,如果内容相同,Java 认为它们代表同一个 String 对象。而用关键字 new 调用构造器,总是会创建一个新的对象,无论内容是否相同。 至于为什么要把 String 类设计成不可变类,是它的用途决定的。其实不只 String,很多 Java 标准类库中的类都是不可变的。在开发一个系统的时候,我们
有时候也需要设计不可变类,来传递一组相关的值,这也是面向对象思想的体现。不可变类有一些优点,比如因为它的对象是只读的,所以多线程并发访问也不会有任何问题。当然也有一些缺点,比如每个不同的状态都要一个对象来代表,可能会造成性能上的问题。所以 Java 标准类库还提供了一个可变版本,即 StringBuffer。

三、Java中的多态

1.Java中实现多态的机制是什么?

靠的是父类或接口定义的引用变量可以指向子类或具体实现的实例对象,而程序调用的方法在运行期才会动态绑定,就是引用变量所指向的具体实例对象的方法,也就是内存里正在运行的那个对象的方法、而不是引用变量的类型中定义的方法。

四、Java 的异常处理

1)按照异常需要处理的时机分为编译时异常(也叫强制性异常)也叫 CheckedException 和运行时异常
(也叫非强制性异常)也叫 RuntimeException。只有 java 语言提供了 Checked 异常,Java 认为 Checked
异常都是可以被处理的异常,所以 Java 程序必须显式处理 Checked 异常。如果程序没有处理 Checked 异常,该程序在编译时就会发生错误无法编译。这体现了 Java 的设计哲学:没有完善错误处理的代码根本没有机会被执行。对 Checked 异常处理方法有两种:
1 当前方法知道如何处理该异常,则用 try...catch 块来处理该异常。
2 当前方法不知道如何处理,则在定义该方法是声明抛出该异常。
运行时异常只有当代码在运行时才发行的异常,编译时不需要 try catch。Runtime 如除数是 0 和数组下标越界等,其产生频繁,处理麻烦,若显示申明或者捕获将会对程序的可读性和运行效率影响很大。所以由系统自动检测并将它们交给缺省的异常处理程序。当然如果你有处理要求也可以显示捕获它们。

2. 调用下面的方法,得到的返回值是什么?

public int getNum(){
try {
int a = 1/0;
return 1;
} catch (Exception e) {
return 2;
}finally{
return 3;
}

代码在走到第 3 行的时候遇到了一个 MathException,这时第四行的代码就不会执行了,代码直接跳转到 catch语句中,走到第 6 行的时候,异常机制有这么一个原则如果在 catch 中遇到了 return 或者异常等能使该函数终止的话那么有 finally 就必须先执行完 finally 代码块里面的代码然后再返回值。因此代码又跳到第 8 行,可惜第 8 行是一个return 语句,那么这个时候方法就结束了,因此第 6 行的返回结果就无法被真正返回。如果 finally 仅仅是处理了一个释放资源的操作,那么该道题最终返回的结果就是 2。因此上面返回值是 3。

3. error 和 exception 的区别?

Error 类和 Exception 类的父类都是 Throwable 类,他们的区别如下。
Error 类一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢出等。对于这类
错误的导致的应用程序中断,仅靠程序本身无法恢复和和预防,遇到这样的错误,建议让程序终止。
Exception 类表示程序可以处理的异常,可以捕获且可能恢复。遇到这类异常,应该尽可能处理异常,使程序恢复
运行,而不应该随意终止异常。
Exception 类又分为运行时异常(Runtime Exception)和受检查的异常(Checked Exception ),运行时异常;ArithmaticException,IllegalArgumentException,编译能通过,但是一运行就终止了,程序不会处理运行时异常,出现这类异常,程序会终止。而受检查的异常,要么用 try。。。catch 捕获,要么用 throws 字句声明抛出,交给它的父类处理,否则编译不会通过。

4. java 异常处理机制

Java 对异常进行了分类,不同类型的异常分别用不同的 Java 类表示,所有异常的根类为 java.lang.Throwable,
Throwable 下面又派生了两个子类:Error 和 Exception,Error 表示应用程序本身无法克服和恢复的一种严重问题。
Exception 表示程序还能够克服和恢复的问题,其中又分为系统异常和普通异常,系统异常是软件本身缺陷所导致的
问题,也就是软件开发人员考虑不周所导致的问题,软件使用者无法克服和恢复这种问题,但在这种问题下还可以让软件系统继续运行或者让软件死掉,例如,数组脚本越界(ArrayIndexOutOfBoundsException),空指针异常(NullPointerException)、类转换异常(ClassCastException);普通异常是运行环境的变化或异常所导致的问题,是用户能够克服的问题,例如,网络断线,硬盘空间不够,发生这样的异常后,程序不应该死掉。
java 为系统异常和普通异常提供了不同的解决方案,编译器强制普通异常必须 try..catch 处理或用 throws 声明继续抛给上层调用方法处理,所以普通异常也称为 checked 异常,而系统异常可以处理也可以不处理,所以,编译器不强制用 try..catch 处理或用 throws 声明,所以系统异常也称为 unchecked 异常。

5. 请写出你最常见的 5 个 RuntimeException

下面列举几个常见的 RuntimeException。
1)java.lang.NullPointerException 空指针异常;出现原因:调用了未经初始化的对象或者是不存在的对象。 2)java.lang.ClassNotFoundException 指定的类找不到;出现原因:类的名称和路径加载错误;通常都是程序
试图通过字符串来加载某个类时可能引发异常。 3)java.lang.NumberFormatException 字符串转换为数字异常;出现原因:字符型数据中包含非数字型字符。 4)java.lang.IndexOutOfBoundsException 数组角标越界异常,常见于操作数组对象时发生。
5)java.lang.IllegalArgumentException 方法传递参数错误。
6)java.lang.ClassCastException 数据类型转换异常。
7)java.lang.NoClassDefFoundException 未找到类定义错误。
8)SQLException SQL 异常,常见于操作数据库时的 SQL 语句错误。
9)java.lang.InstantiationException 实例化异常。
10)java.lang.NoSuchMethodException 方法不存在异常。

6. throw 和 throws 的区别

throw:
1)throw 语句用在方法体内,表示抛出异常,由方法体内的语句处理。
2)throw 是具体向外抛出异常的动作,所以它抛出的是一个异常实例,执行 throw 一定是抛出了某种异常。
** throws:**
1)throws 语句是用在方法声明后面,表示如果抛出异常,由该方法的调用者来进行异常的处理。
2)throws 主要是声明这个方法会抛出某种类型的异常,让它的使用者要知道需要捕获的异常的类型。 3)throws 表示出现异常的一种可能性,并不一定会发生这种异常

7. final、finally、finalize 的区别?

1)final:用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,被其修饰的类不可继承。
2)finally:异常处理语句结构的一部分,表示总是执行。
3)finalize:Object 类的一个方法,在垃圾回收器执行的时候会调用被回收对象的此方法,可以覆盖此方法
提供垃圾收集时的其他资源回收,例如关闭文件等。该方法更像是一个对象生命周期的临终方法,当该方法
被系统调用则代表该对象即将“死亡”,但是需要注意的是,我们主动行为上去调用该方法并不会导致该对
象“死亡”,这是一个被动的方法(其实就是回调方法),不需要我们调用。

五、JavaSE 常用 API

1. Math.round(11.5)等于多少?Math.round(- 11.5) 又等于多少?(2017-11- 14-wl)

Math.round(11.5)的返回值是 12,Math.round(-11.5)的返回值是-11。四舍五入的原理是在参数上加 0.5然后进行取整

2. switch 是否能作用在 byte 上,是否能作用在 long 上,是否能作用在 String上?

Java5 以前 switch(expr)中,expr 只能是 byte、short、char、int。从 Java 5 开始,Java 中引入了枚举类型,
expr 也可以是 enum 类型。
从 Java 7 开始,expr 还可以是字符串(String),但是长整型(long)在目前所有的版本中都是不可以的。

3. 数组有没有 length() 方法?String 有没有 length() 方法?

数组没有 length()方法,而是有 length 的属性。String 有 length()方法。JavaScript 中,获得字符串的长度是
通过 length 属性得到的,这一点容易和 Java 混淆。

4. String 、StringBuilder 、StringBuffer 的区别?

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

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

如上,字符串 str 明明是可以改变的呀!其实不然,str 仅仅是一个引用对象,它指向一个字符串对象“abc”。第二行代码的含义是让 str 重新指向了一个新的字符串“bcd”对象,而“abc”对象并没有任何改变,只不过该对象已经成为一个不可及对象罢了
2)StringBuffer/StringBuilder 表示的字符串对象可以直接进行修改。
3)StringBuilder 是 Java5 中引入的,它和 StringBuffer 的方法完全相同,区别在于它是在单线程环境下使用的,因为它的所有方法都没有被 synchronized 修饰,因此它的效率理论上也比 StringBuffer 要高

5. 什么情况下用“+”运算符进行字符串连接比调用 StringBuffer/StringBuilder 对象的 append 方法连接字符串性能更好?

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

String s = "abc" + "ddd";

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

1.package string;
2.
3. public class TestSimplePlus
4. {
5. public static void main(String[] args)
6. {
7. String s = "abc";
8. String ss = "ok" + s + "xyz" + 5;
9. System.out.println(ss);
10. }
11. }

上面的代码将会输出正确的结果。从表面上看,对字符串和整型使用"+"号并没有什么区别,但事实真的如此吗?
下面让我们来看看这段代码的本质。
我们首先使用反编译工具(如 jdk 带的 javap、或 jad)将 TestSimplePlus 反编译成 Java Byte Code,其中的奥
秘就一目了然了。在本文将使用 jad 来反编译,命令如下:
jad -o -a -s d.java TestSimplePlus.class
反编译后的代码如下:

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

读者可能看到上面的 Java 字节码感到迷糊,不过大家不必担心。本文的目的并不是讲解 Java Byte Code,因此,
并不用了解具体的字节码的含义。
使用 jad 反编译的好处之一就是可以同时生成字节码和源代码。这样可以进行对照研究。从上面的代码很容易看
出,虽然在源程序中使用了"+",但在编译时仍然将"+"转换成 StringBuilder。因此,我们可以得出结论,在 Java 中
无论使用何种方式进行字符串连接,实际上都使用的是 StringBuilder。
那么是不是可以根据这个结论推出使用"+"和 StringBuilder 的效果是一样的呢?这个要从两个方面的解释。如果
从运行结果来解释,那么"+"和 StringBuilder 是完全等效的。但如果从运行效率和资源消耗方面看,那它们将存在很
大的区别。
当然,如果连接字符串行表达式很简单(如上面的顺序结构),那么"+"和 StringBuilder 基本是一样的,但如果
结构比较复杂,如使用循环来连接字符串,那么产生的 Java Byte Code 就会有很大的区别。先让我们看看如下的代
码:

1. package string;
2.
3. import java.util.*;
4.
5. public class TestComplexPlus
6. {
7. public static void main(String[] args)
8. {
9. String s = "";
10. Random rand = new Random();
11. for (int i = 0; i < 10; i++)
12. {
13. s = s + rand.nextInt(1000) + " ";
14. }
15. System.out.println(s);
16. }
17. }

上面的代码返编译后的 Java Byte Code 如下:

1. package string;
2.
3. import java.io.PrintStream;
4. import java.util.Random;
5.
6. public class TestComplexPlus
7. {
8.
9. public TestComplexPlus()
10. {
11. // 0 0:aload_0
12. // 1 1:invokespecial #8 <Method void Object()>
13. // 2 4:return
14. }
15.
16. public static void main(String args[])
17. {
18. String s = "";
19. // 0 0:ldc1 #16 <String "">
20. // 1 2:astore_1
21. Random rand = new Random();
22. // 2 3:new #18 <Class Random>
23. // 3 6:dup
24. // 4 7:invokespecial #20 <Method void Random()>
25. // 5 10:astore_2
26. for(int i = 0; i < 10; i++)
27. //* 6 11:iconst_0
28. //* 7 12:istore_3
29. //* 8 13:goto 49
30. s = (new StringBuilder(String.valueOf(s))).append(rand.nextInt(1000)).append(" ").t
oString();
31. // 9 16:new #21 <Class StringBuilder>
32. // 10 19:dup
33. // 11 20:aload_1
34. // 12 21:invokestatic #23 <Method String String.valueOf(Object)>
35. // 13 24:invokespecial #29 <Method void StringBuilder(String)>
36. // 14 27:aload_2
37. // 15 28:sipush 1000
38. // 16 31:invokevirtual #32 <Method int Random.nextInt(int)>
39. // 17 34:invokevirtual #36 <Method StringBuilder StringBuilder.append(int)>
40. // 18 37:ldc1 #40 <String " ">
41. // 19 39:invokevirtual #42 <Method StringBuilder StringBuilder.append(String)>
42. // 20 42:invokevirtual #45 <Method String StringBuilder.toString()>
43. // 21 45:astore_1
44.
45. // 22 46:iinc 3 1
46. // 23 49:iload_3
47. // 24 50:bipush 10
48. // 25 52:icmplt 16
49. System.out.println(s);
50. // 26 55:getstatic #49 <Field PrintStream System.out>
51. // 27 58:aload_1
52. // 28 59:invokevirtual #55 <Method void PrintStream.println(String)>
53. // 29 62:return
54. }
55. }

大家可以看到,虽然编译器将"+"转换成了 StringBuilder,但创建 StringBuilder 对象的位置却在 for 语句内
部。这就意味着每执行一次循环,就会创建一个 StringBuilder 对象(对于本例来说,是创建了 10 个 StringBuilder
对象),虽然 Java 有垃圾回收器,但这个回收器的工作时间是不定的。如果不断产生这样的垃圾,那么仍然会占用
大量的资源。解决这个问题的方法就是在程序中直接使用 StringBuilder 来连接字符串,代码如下:

1. package string;
2.
3. import java.util.*;
4.
5. public class TestStringBuilder
6. {
7. public static void main(String[] args)
8. {
9. String s = "";
10. Random rand = new Random();
11. StringBuilder result = new StringBuilder();
12. for (int i = 0; i < 10; i++)
13. {
14. result.append(rand.nextInt(1000));
15. result.append(" ");
16. }
17. System.out.println(result.toString());
18. }
19. }

上面代码反编译后的结果如下:

1. 20.package string;
2.
3. import java.io.PrintStream;
4. import java.util.Random;
5.
6. public class TestStringBuilder
7. {
8.
9. public TestStringBuilder()
10. {
11. // 0 0:aload_0
12. // 1 1:invokespecial #8 <Method void Object()>
13. // 2 4:return
14. }
15.
16. public static void main(String args[])
17. {
18. String s = "";
19. // 0 0:ldc1 #16 <String "">
20. // 1 2:astore_1
21. Random rand = new Random();
22. // 2 3:new #18 <Class Random>
23. // 3 6:dup
24. // 4 7:invokespecial #20 <Method void Random()>
25. // 5 10:astore_2
26. StringBuilder result = new StringBuilder();
27. // 6 11:new #21 <Class StringBuilder>
28. // 7 14:dup
29. // 8 15:invokespecial #23 <Method void StringBuilder()>
30. // 9 18:astore_3
31. for(int i = 0; i < 10; i++)
32. //* 10 19:iconst_0
33. //* 11 20:istore 4
34. //* 12 22:goto 47
35. {
36. result.append(rand.nextInt(1000));
37. // 13 25:aload_3
38. // 14 26:aload_2
39. // 15 27:sipush 1000
40. // 16 30:invokevirtual #24 <Method int Random.nextInt(int)>
41. // 17 33:invokevirtual #28 <Method StringBuilder StringBuilder.append(int)>
42. // 18 36:pop
43. result.append(" ");
44. // 19 37:aload_3
45. // 20 38:ldc1 #32 <String " ">
46. // 21 40:invokevirtual #34 <Method StringBuilder StringBuilder.append(String)>
47. // 22 43:pop
48. }
49.
50. // 23 44:iinc 4 1
51. // 24 47:iload 4
52. // 25 49:bipush 10
53. // 26 51:icmplt 25
54. System.out.println(result.toString());
55. // 27 54:getstatic #37 <Field PrintStream System.out>
56. // 28 57:aload_3
57. // 29 58:invokevirtual #43 <Method String StringBuilder.toString()>
58. // 30 61:invokevirtual #47 <Method void PrintStream.println(String)>
59. // 31 64:return
60. }
61. }

从上面的反编译结果可以看出,创建 StringBuilder 的代码被放在了 for 语句外。虽然这样处理在源程序中看起
来复杂,但却换来了更高的效率,同时消耗的资源也更少了。
在使用 StringBuilder 时要注意,尽量不要"+"和 StringBuilder 混着用,否则会创建更多的 StringBuilder 对
象,如下面代码所:

for (int i = 0; i < 10; i++)
{
result.append(rand.nextInt(1000));
result.append(" ");
}

改成如下形式:

for (int i = 0; i < 10; i++)
{
result.append(rand.nextInt(1000) + " ");
}

则反编译后的结果如下:

for(int i = 0; i < 10; i++)
//* 10 19:iconst_0
//* 11 20:istore 4
//* 12 22:goto 65
{
result.append((new StringBuilder(String.valueOf(rand.nextInt(1000)))).append(" ").toString());
// 13 25:aload_3
// 14 26:new #21 <Class StringBuilder>
// 15 29:dup

从上面的代码可以看出,Java 编译器将"+"编译成了 StringBuilder,这样 for 语句每循环一次,又创建了一个
StringBuilder 对象。
如果将上面的代码在 JDK1.4 下编译,必须将 StringBuilder 改为 StringBuffer,而 JDK1.4 将"+"转换为
StringBuffer(因为 JDK1.4 并没有提供 StringBuilder 类)。StringBuffer 和 StringBuilder 的功能基本一样,只是
StringBuffer 是线程安全的,而 StringBuilder 不是线程安全的。因此,StringBuilder 的效率会更高。

6. 请说出下面程序的输出

补充:解答上面的面试题需要知道如下两个知识点:

1. class StringEqualTest {
2. public static void main(String[] args) {
3. String s1 = "Programming";
4. String s2 = new String("Programming");
5. String s3 = "Program";
6. String s4 = "ming";
7. String s5 = "Program" + "ming";
8. String s6 = s3 + s4;
9. System.out.println(s1 == s2); //false
10. System.out.println(s1 == s5); //true
11. System.out.println(s1 == s6); //false
12. System.out.println(s1 == s6.intern()); //true
13. System.out.println(s2 == s2.intern()); //false
14. }
15.}
  1. String 对象的 intern()方法会得到字符串对象在常量池中对应的版本的引用(如果常量池中有一个字符串与
    String 对象的 equals 结果是 true),如果常量池中没有对应的字符串,则该字符串将被添加到常量池中,然后返
    回常量池中字符串的引用;
  2. 字符串的+操作其本质是创建了 StringBuilder 对象进行 append 操作,然后将拼接后的 StringBuilder 对
    象用 toString 方法处理成 String 对象,这一点可以用 javap -c StringEqualTest.class 命令获得 class 文件对应的 JVM 字节码指令就可以看出来。

7. Java 中的日期和时间

7.1 如何取得年月日、小时分钟秒?

1. public class DateTimeTest {
2. public static void main(String[] args) {
3. Calendar cal = Calendar.getInstance();
4. System.out.println(cal.get(Calendar.YEAR));
5. System.out.println(cal.get(Calendar.MONTH)); // 0 - 11
6. System.out.println(cal.get(Calendar.DATE));
7. System.out.println(cal.get(Calendar.HOUR_OF_DAY));
8. System.out.println(cal.get(Calendar.MINUTE));
9. System.out.println(cal.get(Calendar.SECOND));
10. // Java 8
11. LocalDateTime dt = LocalDateTime.now();
12. System.out.println(dt.getYear());
13. System.out.println(dt.getMonthValue()); // 1 - 12
14. System.out.println(dt.getDayOfMonth());
15. System.out.println(dt.getHour());
16. System.out.println(dt.getMinute());
17. System.out.println(dt.getSecond());
18. }
19.}

7.2 如何取得从 1970 年 1 月 1 日 0 时 0 分 0 秒到现在的毫秒数?

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

7.3 如何取得某月的最后一天?

1. //获取当前月第一天:
2. Calendar c = Calendar.getInstance();
3. c.add(Calendar.MONTH, 0);
4. c.set(Calendar.DAY_OF_MONTH,1);//设置为 1 号,当前日期既为本月第一天
5. String first = format.format(c.getTime());
6. System.out.println("===============first:"+first);
7.
8. //获取当前月最后一天
9. Calendar ca = Calendar.getInstance();
10. ca.set(Calendar.DAY_OF_MONTH, ca.getActualMaximum(Calendar.DAY_OF_MONTH));
11. String last = format.format(ca.getTime());
12. System.out.println("===============last:"+last);
13.
14. //Java 8
15. LocalDate today = LocalDate.now();
16. //本月的第一天
17. LocalDate firstday = LocalDate.of(today.getYear(),today.getMonth(),1);
18. //本月的最后一天
19. LocalDate lastDay =today.with(TemporalAdjusters.lastDayOfMonth());
20. System.out.println("本月的第一天"+firstday);
21. System.out.println("本月的最后一天"+lastDay);

7.4 如何格式化日期?

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

1. import java.text.SimpleDateFormat;
2. import java.time.LocalDate;
3. import java.time.format.DateTimeFormatter;
4. import java.util.Date;
5. class DateFormatTest {
6.
7. public static void main(String[] args) {
8. SimpleDateFormat oldFormatter = new SimpleDateFormat("yyyy/MM/dd");
9. Date date1 = new Date();
10. System.out.println(oldFormatter.format(date1));
11.
12. // Java 8
13. DateTimeFormatter newFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
14. LocalDate date2 = LocalDate.now();
15. System.out.println(date2.format(newFormatter));
16. }
17. }

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

7.5 打印昨天的当前时刻?

1. import java.util.Calendar;
2. class YesterdayCurrent {
3. public static void main(String[] args){
4. Calendar cal = Calendar.getInstance();
5. cal.add(Calendar.DATE, -1);
6. System.out.println(cal.getTime());
7. }
8. }
9.
10.
11. //java-8
12. import java.time.LocalDateTime;
13. class YesterdayCurrent {
14. public static void main(String[] args) {
15. LocalDateTime today = LocalDateTime.now();
16. LocalDateTime yesterday = today.minusDays(1);
17. System.out.println(yesterday);
18. }
19. }

7.6 Java8 的日期特性?

Java 8 日期/时间特性
Java 8 日期/时间 API 是 JSR-310 的实现,它的实现目标是克服旧的日期时间实现中所有的缺陷,新的日期/时间
API 的一些设计原则是:
 不变性:新的日期/时间 API 中,所有的类都是不可变的,这对多线程环境有好处。
 关注点分离:新的 API 将人可读的日期时间和机器时间(unix timestamp)明确分离,它为日期(Date)、时间
(Time)、日期时间(DateTime)、时间戳(unix timestamp)以及时区定义了不同的类。
 清晰:在所有的类中,方法都被明确定义用以完成相同的行为。举个例子,要拿到当前实例我们可以使用 now()方法,在所有的类中都定义了 format()和 parse()方法,而不是像以前那样专门有一个独立的类。为了更好的处理问题,所有的类都使用了工厂模式和策略模式,一旦你使用了其中某个类的方法,与其他类协同工作并不困难。
 实用操作:所有新的日期/时间 API 类都实现了一系列方法用以完成通用的任务,如:加、减、格式化、解析、从日期/时间中提取单独部分,等等。
 可扩展性:新的日期/时间 API 是工作在 ISO-8601 日历系统上的,但我们也可以将其应用在非 ISO 的日历上。
Java 8 日期/时间 API 包解释
 java.time 包:这是新的 Java 日期/时间 API 的基础包,所有的主要基础类都是这个包的一部分,如:LocalDate,
LocalTime, LocalDateTime, Instant, Period, Duration 等等。所有这些类都是不可变的和线程安全的,在绝大多
数情况下,这些类能够有效地处理一些公共的需求。
 java.time.chrono 包:这个包为非 ISO 的日历系统定义了一些泛化的 API,我们可以扩展 AbstractChronology
类来创建自己的日历系统。
 java.time.format 包:这个包包含能够格式化和解析日期时间对象的类,在绝大多数情况下,我们不应该直接使用它们,因为 java.time 包中相应的类已经提供了格式化和解析的方法。
 java.time.temporal 包:这个包包含一些时态对象,我们可以用其找出关于日期/时间对象的某个特定日期或时间,
比如说,可以找到某月的第一天或最后一天。你可以非常容易地认出这些方法,因为它们都具有“withXXX”的格式。
 java.time.zone 包:这个包包含支持不同时区以及相关规则的类
Java 8 日期/时间常用 API
1.java.time.LocalDate
LocalDate 是一个不可变的类,它表示默认格式(yyyy-MM-dd)的日期,我们可以使用 now()方法得到当前时间,
也可以提供输入年份、月份和日期的输入参数来创建一个 LocalDate 实例。该类为 now()方法提供了重载方法,我们
可以传入 ZoneId 来获得指定时区的日期。该类提供与 java.sql.Date 相同的功能,对于如何使用该类,我们来看一个
简单的例子

package com.journaldev.java8.time;
import java.time.LocalDate;
import java.time.Month;
import java.time.ZoneId;
/**
* LocalDate Examples
* @author pankaj
*
*/
public class LocalDateExample {
public static void main(String[] args) {
//Current Date
LocalDate today = LocalDate.now();
System.out.println("Current Date="+today);
//Creating LocalDate by providing input arguments
LocalDate firstDay_2014 = LocalDate.of(2014, Month.JANUARY, 1);
System.out.println("Specific Date="+firstDay_2014);
//Try creating date by providing invalid inputs
//LocalDate feb29_2014 = LocalDate.of(2014, Month.FEBRUARY, 29);
//Exception in thread "main" java.time.DateTimeException:
//Invalid date 'February 29' as '2014' is not a leap year
//Current date in "Asia/Kolkata", you can get it from ZoneId javadoc
LocalDate todayKolkata = LocalDate.now(ZoneId.of("Asia/Kolkata"));
System.out.println("Current Date in IST="+todayKolkata);
//java.time.zone.ZoneRulesException: Unknown time-zone ID: IST
//LocalDate todayIST = LocalDate.now(ZoneId.of("IST"));
//Getting date from the base date i.e 01/01/1970
LocalDate dateFromBase = LocalDate.ofEpochDay(365);
System.out.println("365th day from base date= "+dateFromBase);
LocalDate hundredDay2014 = LocalDate.ofYearDay(2014, 100);
System.out.println("100th day of 2014="+hundredDay2014);
} }
输出:
Current Date=2014-04-28
Specific Date=2014-01-01
Current Date in IST=2014-04-29
365th day from base date= 1971-01-01
100th day of 2014=2014-04-10

2.java.time.LocalTime
LocalTime 是一个不可变的类,它的实例代表一个符合人类可读格式的时间,默认格式是 hh:mm:ss.zzz。像
LocalDate 一样,该类也提供了时区支持,同时也可以传入小时、分钟和秒等输入参数创建实例,我们来看一个简单的
程序,演示该类的使用方法

package com.journaldev.java8.time;
import java.time.LocalTime;
import java.time.ZoneId;
/**
* LocalTime Examples
*/
public class LocalTimeExample {
public static void main(String[] args) {
//Current Time
LocalTime time = LocalTime.now();
System.out.println("Current Time="+time);
//Creating LocalTime by providing input arguments
LocalTime specificTime = LocalTime.of(12,20,25,40);
System.out.println("Specific Time of Day="+specificTime);
//Try creating time by providing invalid inputs
//LocalTime invalidTime = LocalTime.of(25,20);
//Exception in thread "main" java.time.DateTimeException:
//Invalid value for HourOfDay (valid values 0 - 23): 25
//Current date in "Asia/Kolkata", you can get it from ZoneId javadoc
LocalTime timeKolkata = LocalTime.now(ZoneId.of("Asia/Kolkata"));
System.out.println("Current Time in IST="+timeKolkata);
//java.time.zone.ZoneRulesException: Unknown time-zone ID: IST
//LocalTime todayIST = LocalTime.now(ZoneId.of("IST"));
//Getting date from the base date i.e 01/01/1970
LocalTime specificSecondTime = LocalTime.ofSecondOfDay(10000);
System.out.println("10000th second time= "+specificSecondTime);
} }
输出:
Current Time=15:51:45.240
Specific Time of Day=12:20:25.000000040
Current Time in IST=04:21:45.276
10000th second time= 02:46:40

3. java.time.LocalDateTime
LocalDateTime 是一个不可变的日期-时间对象,它表示一组日期-时间,默认格式是 yyyy-MM-dd-HH-mmss.zzz。它提供了一个工厂方法,接收 LocalDate 和 LocalTime 输入参数,创建 LocalDateTime 实例。我们来看一个
简单的例子。

package com.journaldev.java8.time;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
import java.time.ZoneId;
import java.time.ZoneOffset;
public class LocalDateTimeExample {
public static void main(String[] args) {
//Current Date
LocalDateTime today = LocalDateTime.now();
System.out.println("Current DateTime="+today);
//Current Date using LocalDate and LocalTime
today = LocalDateTime.of(LocalDate.now(), LocalTime.now());
System.out.println("Current DateTime="+today);
//Creating LocalDateTime by providing input arguments
LocalDateTime specificDate = LocalDateTime.of(2014, Month.JANUARY, 1, 10, 10, 30);
System.out.println("Specific Date="+specificDate);
//Try creating date by providing invalid inputs
//LocalDateTime feb29_2014 = LocalDateTime.of(2014, Month.FEBRUARY, 28, 25,1,1);
//Exception in thread "main" java.time.DateTimeException:
//Invalid value for HourOfDay (valid values 0 - 23): 25
//Current date in "Asia/Kolkata", you can get it from ZoneId javadoc
LocalDateTime todayKolkata = LocalDateTime.now(ZoneId.of("Asia/Kolkata"));
System.out.println("Current Date in IST="+todayKolkata);
//java.time.zone.ZoneRulesException: Unknown time-zone ID: IST
//LocalDateTime todayIST = LocalDateTime.now(ZoneId.of("IST"));
//Getting date from the base date i.e 01/01/1970
LocalDateTime dateFromBase = LocalDateTime.ofEpochSecond(10000, 0, ZoneOffset.UTC);
System.out.println("10000th second time from 01/01/1970= "+dateFromBase);
} }
输出:
Current DateTime=2014-04-28T16:00:49.455
Current DateTime=2014-04-28T16:00:49.493
Specific Date=2014-01-01T10:10:30
Current Date in IST=2014-04-29T04:30:49.493
10000th second time from 01/01/1970= 1970-01-01T02:46:40

在所有这三个例子中,我们已经看到如果我们提供了无效的参数去创建日期 /时间,那么系统会抛出
java.time.DateTimeException,这是一种运行时异常,我们并不需要显式地捕获它。
同时我们也看到,能够通过传入 ZoneId 得到日期/时间数据,你可以从它的 Javadoc 中得到支持的 Zoneid 的列
表,当运行以上类时,可以得到以上输出。
4. java.time.Instant
Instant 类是用在机器可读的时间格式上的,它以 Unix 时间戳的形式存储日期时间,我们来看一个简单的程序

package com.journaldev.java8.time;
import java.time.Duration;
public class InstantExample {
public static void main(String[] args) {
//Current timestamp
Instant timestamp = Instant.now();
System.out.println("Current Timestamp = "+timestamp);
//Instant from timestamp
Instant specificTime = Instant.ofEpochMilli(timestamp.toEpochMilli());
System.out.println("Specific Time = "+specificTime);
//Duration example
Duration thirtyDay = Duration.ofDays(30);
System.out.println(thirtyDay);
} }
输出:
Current Timestamp = 2014-04-28T23:20:08.489Z
Specific Time = 2014-04-28T23:20:08.489Z
PT720H
  1. 日期 API 工具
    我们早些时候提到过,大多数日期/时间 API 类都实现了一系列工具方法,如:加/减天数、周数、月份数,等等。
    还有其他的工具方法能够使用 TemporalAdjuster 调整日期,并计算两个日期间的周期。
package com.journaldev.java8.time;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.Period;
import java.time.temporal.TemporalAdjusters;
public class DateAPIUtilities {
public static void main(String[] args) {
LocalDate today = LocalDate.now();
//Get the Year, check if it's leap year
System.out.println("Year "+today.getYear()+" is Leap Year? "+today.isLeapYear());
import java.time.Instant;
//Compare two LocalDate for before and after
System.out.println("Today is before 01/01/2015? "+today.isBefore(LocalDate.of(2015,1,1)));
//Create LocalDateTime from LocalDate
System.out.println("Current Time="+today.atTime(LocalTime.now()));
//plus and minus operations
System.out.println("10 days after today will be "+today.plusDays(10));
System.out.println("3 weeks after today will be "+today.plusWeeks(3));
System.out.println("20 months after today will be "+today.plusMonths(20));
System.out.println("10 days before today will be "+today.minusDays(10));
System.out.println("3 weeks before today will be "+today.minusWeeks(3));
System.out.println("20 months before today will be "+today.minusMonths(20));
//Temporal adjusters for adjusting the dates
System.out.println("First date of this month= "+today.
with(TemporalAdjusters.firstDayOfMonth()));
LocalDate lastDayOfYear = today.with(TemporalAdjusters.lastDayOfYear());
System.out.println("Last date of this year= "+lastDayOfYear);
Period period = today.until(lastDayOfYear);
System.out.println("Period Format= "+period);
System.out.println("Months remaining in the year= "+period.getMonths());
} }
输出:
Year 2014 is Leap Year? false
Today is before 01/01/2015? true
Current Time=2014-04-28T16:23:53.154
10 days after today will be 2014-05-08
3 weeks after today will be 2014-05-19
20 months after today will be 2015-12-28
10 days before today will be 2014-04-18
3 weeks before today will be 2014-04-07
20 months before today will be 2012-08-28
First date of this month= 2014-04-01
Last date of this year= 2014-12-31
Period Format= P8M3D
Months remaining in the year= 8

6. 解析和格式化
将一个日期格式转换为不同的格式,之后再解析一个字符串,得到日期时间对象,这些都是很常见的。我们来看一
下简单的例子。

package com.journaldev.java8.time;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class DateParseFormatExample {
public static void main(String[] args) {
//Format examples
LocalDate date = LocalDate.now();
//default format
System.out.println("Default format of LocalDate="+date);
//specific format
System.out.println(date.format(DateTimeFormatter.ofPattern("d::MMM::uuuu")));
System.out.println(date.format(DateTimeFormatter.BASIC_ISO_DATE));
LocalDateTime dateTime = LocalDateTime.now();
//default format
System.out.println("Default format of LocalDateTime="+dateTime);
//specific format
System.out.println(dateTime.format(DateTimeFormatter.ofPattern("d::MMM::uuuu HH::mm::ss")));
System.out.println(dateTime.format(DateTimeFormatter.BASIC_ISO_DATE));
Instant timestamp = Instant.now();
//default format
System.out.println("Default format of Instant="+timestamp);
//Parse examples
LocalDateTime dt = LocalDateTime.parse("27::Apr::2014 21::39::48",
DateTimeFormatter.ofPattern("d::MMM::uuuu HH::mm::ss"));
System.out.println("Default format after parsing = "+dt);
} }
输出:
Default format of LocalDate=2014-04-28
28::Apr::2014
20140428
Default format of LocalDateTime=2014-04-28T16:25:49.341
28::Apr::2014 16::25::49
20140428
Default format of Instant=2014-04-28T23:25:49.342Z
Default format after parsing = 2014-04-27T21:39:48

7. 旧的日期时间支持
旧的日期/时间类已经在几乎所有的应用程序中使用,因此做到向下兼容是必须的。这也是为什么会有若干工具方法帮助我们将旧的类转换为新的类,反之亦然。我们来看一下简单的例子。

六、Java 的数据类型

1.Java的基本数据类型都有哪些各占几个字节

image

2. String 是基本数据类型吗?

String 是引用类型,底层用 char 数组实现的。
后面底层是byte数组实现。

3. short s1 = 1; s1 = s1 + 1; 有错吗?short s1 = 1; s1 += 1 有错吗;

前者不正确,后者正确。对于 short s1 = 1; s1 = s1 + 1;由于 1 是 int 类型,因此 s1+1 运算结果也是 int 型,
需要强制转换类型才能赋值给 short 型。而 short s1 = 1; s1 += 1;可以正确编译,因为 s1+= 1;相当于 s1 = (short)(s1 + 1);其中有隐含的强制类型转换。

4. int 和 和 Integer 有什么区别?

Java 是一个近乎纯洁的面向对象编程语言,但是为了编程的方便还是引入了基本数据类型,为了能够将这些基本数据类型当成对象操作,Java 为每一个基本数据类型都引入了对应的包装类型(wrapper class),int 的包装类就是Integer,从Java 5开始引入了自动装箱/拆箱机制,使得二者可以相互转换。
Java 为每个原始类型提供了包装类型:

  • 原始类型: boolean,char,byte,short,int,long,float,double
  • 包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double
    image

5. 下面 Integer 类型的数值比较输出的结果为?

image
如果不明就里很容易认为两个输出要么都是 true 要么都是 false。首先需要注意的是 f1、f2、f3、f4 四个变量
都是 Integer 对象引用,所以下面的运算比较的不是值而是引用。装箱的本质是什么呢?当我们给一个 Integer 对
象赋一个 int 值的时候,会调用 Integer 类的静态方法 valueOf,如果看看 valueOf 的源代码就知道发生了什么。
源码:
image
IntegerCache 是 Integer 的内部类,其代码如下所示:
image
image
简单的说,如果整型字面量的值在-128 到 127 之间,那么不会 new 新的 Integer 对象,而是直接引用常量池
中的 Integer 对象,所以上面的面试题中 f1
f2 的结果是 true,而 f3==f4 的结果是 false。
提醒:越是貌似简单的面试题其中的玄机就越多,需要面试者有相当深厚的功力。

6. String 类常用方法

image

7. String、StringBuffer、StringBuilder 的区别?

(1)、可变不可变
String:字符串常量,在修改时不会改变自身;若修改,等于重新生成新的字符串对象。
StringBuffer:在修改时会改变对象自身,每次操作都是对 StringBuffer 对象本身进行修改,不是生成新的对象;使用场景:对字符串经常改变情况下,主要方法:append(),insert()等。
(2)、线程是否安全
String:对象定义后不可变,线程安全。
StringBuffer:是线程安全的(对调用方法加入同步锁),执行效率较慢,适用于多线程下操作字符串缓冲区大量数据。
StringBuilder:是线程不安全的,适用于单线程下操作字符串缓冲区大量数据。
(3)、共同点
StringBuilder 与 StringBuffer 有公共父类 AbstractStringBuilder(抽象类)。
StringBuilder、StringBuffer 的方法都会调用 AbstractStringBuilder 中的公共方法,如 super.append(...)。
只是 StringBuffer 会在方法上加 synchronized 关键字,进行同步。最后,如果程序不是多线程的,那么使用StringBuilder 效率高于 StringBuffer。

8. 数据类型之间的转换

(1)、字符串如何转基本数据类型?
调用基本数据类型对应的包装类中的方法 parseXXX(String)或 valueOf(String)即可返回相应基本类型。 (2)、基本数据类型如何转字符串?
一种方法是将基本数据类型与空字符串(“”)连接(+)即可获得其所对应的字符串;另一种方法是调用 String
类中的 valueOf()方法返回相应字符串。

七、Java 的 IO

1. Java 中有几种类型的流

按照流的方向:输入流(inputStream)和输出流(outputStream)。
按照实现功能分:节点流(可以从或向一个特定的地方(节点)读写数据。如 FileReader)和处理流(是对一个
已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。如 BufferedReader。处理流的构造方法总是要
带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。)
按照处理数据的单位:字节流和字符流。字节流继承于 InputStream 和 OutputStream,字符流继承于InputStreamReader 和OutputStreamWriter。
image

2. 字节流如何转为字符流

字节输入流转字符输入流通过 InputStreamReader 实现,该类的构造函数可以传入 InputStream 对象。
字节输出流转字符输出流通过OutputStreamWriter 实现,该类的构造函数可以传入 OutputStream 对象。

3. 如何将一个 java 对象序列化到文件里

在 java 中能够被序列化的类必须先实现 Serializable 接口,该接口没有任何抽象方法只是起到一个标记作用。

1. //对象输出流
2. ObjectOutputStream objectOutputStream =
3. new ObjectOutputStream(new FileOutputStream(new File("D://obj")));
4. objectOutputStream.writeObject(new User("zhangsan", 100));
5. objectOutputStream.close();
6. //对象输入流
7. ObjectInputStream objectInputStream =
8. new ObjectInputStream(new FileInputStream(new File("D://obj")));
9. User user = (User)objectInputStream.readObject();
10. System.out.println(user);
11. objectInputStream.close();

4. 字节流和字符流的区别

字节流读取的时候,读到一个字节就返回一个字节; 字符流使用了字节流读到一个或多个字节(中文对应的字节
数是两个,在 UTF-8 码表中是 3 个字节)时。先去查指定的编码表,将查到的字符返回。 字节流可以处理所有类型数
据,如:图片,MP3,AVI 视频文件,而字符流只能处理字符数据。只要是处理纯文本数据,就要优先考虑使用字符
流,除此之外都用字节流。字节流主要是操作 byte 类型数据,以 byte 数组为准,主要操作类就是 OutputStream、
InputStream
字符流处理的单元为 2 个字节的 Unicode 字符,分别操作字符、字符数组或字符串,而字节流处理单元为 1 个字
节,操作字节和字节数组。所以字符流是由 Java 虚拟机将字节转化为 2 个字节的 Unicode 字符为单位的字符而成的,
所以它对多国语言支持性比较好!如果是音频文件、图片、歌曲,就用字节流好点,如果是关系到中文(文本)的,用
字符流好点。在程序中一个字符等于两个字节,java 提供了 Reader、Writer 两个专门操作字符流的类。

posted @   晚风Yx  阅读(53)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
点击右上角即可分享
微信分享提示