Java基础

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录


前言

16.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;
... public Demo { s = "Initial Value";
}
... }

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


17.是否可以继承 String 类?

String 类是 final 类故不可以继承


18.String s = new String(“xyz”);创建了几个 String Object? 二者之间有什么区

别? xyz 是字面量
两个,一个放在常量区,不管写多少遍,都是同一个。New String 每写一遍,就创建一个新。


19.String 和 StringBuffer 的区别

JAVA 平台提供了两个类:String 和 StringBuffer,它们可以储存和操作字符串,即包含多个字符的字符数据。这个 String 类提供了
数值不可改变的字符串。而这个StringBuffer类提供的字符串进行修改。当你知道字符数据要改变的时候你就可以使用StringBuffer。
典型地,你可以使用 StringBuffers 来动态构造字符数据。另外,String 实现了 equals 方法,new String“( abc”).equals(new String“( abc”)
的结果为 true,而 StringBuffer 没有实现 equals 方法,所以,new StringBuffer“( abc”).equals(new StringBuffer“( abc”)的结果为 false。
接着要举一个具体的例子来说明,我们要把 1 到 100 的所有数字拼起来,组成一个串。

StringBuffer sbf = new StringBuffer();
for(int i=0;i<100;i++)
{ sbf.append(i);
}
//上面的代码效率很高,因为只创建了一个 StringBuffer 对象,而下面的代码效率很低,因为创建了 101 个对象。
String str = new String();
for(int i=0;i<100;i++)
{ str = str + i;
}

20.try {}里有一个 return 语句,那么紧跟在这个 try 后的 finally {}里的 code 会

不会被执行,什么时候被执行,在 return 前还是后?
会执行,在 return 前执行。
我的答案是在 return 中间执行,参看下一题的讲解。

public class Test {
public static void main(String[] args) {
System.out.println(new Test().test());;
}static int test()
{
int x = 1
try
{ return x;//产生中断 保存断点 压栈 x=1
}
finally
{
System.out.println(++x);//接着恢复断点 弹栈 x=1
}
}
}

---------执行结果 --------- 1


21.final, finally, finalize 的区别。

final 用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。
内部类要访问局部变量,局部变量必须定义成 final 类型,例如,一段代码……
finally 是异常处理语句结构的一部分,表示总是执行。
//析构函数
finalize 是 Object 类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回
收,例如关闭文件等。JVM 不保证此方法总被调用


22.运行时异常与一般异常有何异同?

异常表示程序运行过程中可能出现的非正常状态,运行时异常表示虚拟机的通常操作中可能遇到的异常,是一种常见运行错误。java 编译器要求方法必须声明抛出可能发生的非运行时异常,但是并不要求必须声明抛出未被捕获的运行时异常。


23.error 和 exception 有什么区别?

error 表示恢复不是不可能但很困难的情况下的一种严重问题。比如说内存溢出。不可能指望程序能处理这样的情况。 exception 表示
一种设计或实现问题。也就是说,它表示如果程序运行正常,从不会发生的情况。


24.给我一个你最常见到的 runtime exception。

ArithmeticException, ArrayStoreException, BufferOverflowException, BufferUnderflowException, CannotRedoException, CannotUndoException, ClassCastException, CMMException, ConcurrentModificationException, DOMException, EmptyStackException, IllegalArgumentExceptionIllegalMonitorStateException, IllegalPathStateException, IllegalStateException, ImagingOpException, IndexOutOfBoundsException, MissingResourceException, NegativeArraySizeException, NoSuchElementException, NullPointerException, ProfileDataException, ProviderException, RasterFORMatException, SecurityException, SystemException, UndeclaredThrowableException, UnmodifiableSetException, UnsupportedOperationException


25.sleep() 和 wait() 有什么区别?

sleep 是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会
自动恢复。调用 sleep 不会释放对象锁。 wait 是 Object 类的方法,对此对象调用 wait 方法导致本线程放弃对象锁,进入等待此对象的
等待锁定池,只有针对此对象发出 notify 方法(或 notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。
sleep 就是正在执行的线程主动让出 cpu,cpu 去执行其他线程,在 sleep 指定的时间过后,cpu 才会回到这个线程上继续往下执行,如
果当前线程进入了同步锁,sleep 方法并不会释放锁,即使当前线程使用 sleep 方法让出了 cpu,但其他被同步锁挡住了的线程也无法
得到执行。wait 是指在一个已经进入了同步锁的线程内,让自己暂时让出同步锁,以便其他正在等待此锁的线程可以得到同步锁并运
行,只有其他线程调用了 notify 方法(notify 并不释放锁,只是告诉调用过 wait 方法的线程可以去参与获得锁的竞争了,但不是马上
得到锁,因为锁还在别人手里,别人还没释放。如果 notify 方法后面的代码还有很多,需要这些代码执行完后才会释放锁,可以在 notfiy
方法后增加一个等待和一些代码,看看效果),调用 wait 方法的线程就会解除 wait 状态和程序可以再次得到锁后继续向下运行。对于
wait 的讲解一定要配合例子代码来说明,才显得自己真明白。

package com.huawei.interview;
public class MultiThread {
public static void main(String[] args) { new Thread(new Thread1()).start();
try {
Thread.sleep(10);
} catch (InterruptedException e) { e.printStackTrace();
}new Thread(new Thread2()).start();
}
private static class Thread1 implements Runnable
{
@Override public void run() {
//由于这里的 Thread1 和下面的 Thread2 内部 run 方法要用同一对象作为监视器,我们这里不能用 this,因为在 Thread2 里面的 this 和
这个 Thread1 的 this 不是同一个对象。我们用 MultiThread.class 这个字节码对象,当前虚拟机里引用这个变量时,指向的都是同一个对
象。
synchronized (MultiThread.class) {
System.out.println("enter thread1...");
System.out.println("thread1 is waiting");
try {
//释放锁有两种方式,第一种方式是程序自然离开监视器的范围,也就是离开了 synchronized 关键字管辖的代码范围,
另一种方式就是在 synchronized 关键字管辖的代码内部调用监视器对象的 MultiThread.class.wait();
} catch (InterruptedException e) { e.printStackTrace();
}
System.out.println("thread1 is going on...");
System.out.println("thread1 is being over!");
}
}
}
private static class Thread2 implements Runnable
{
@Override public void run() { synchronized (MultiThread.class) {
System.out.println("enter thread2...");
System.out.println("thread2 notify other thread can release wait status..");
//由于 notify 方法并不释放锁, 即使 thread2 调用下面的 sleep 方法休息了 10 毫秒,但 thread1 仍然不会执行,因为 thread2 没有释放
锁,所以 Thread1 无法得不到锁。
MultiThread.class.notify();
System.out.println("thread2 is sleeping ten millisecond...");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block e.printStackTrace();
}
System.out.println("thread2 is going on...");
System.out.println("thread2 is being over!");
}
}
}
}

26.同步和异步有何异同,在什么情况下分别使用他们?举例说明。

如果数据将在线程间共享。例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那
么这些数据就是共享数据,必须进行同步存取。
当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,在很
多情况下采用异步途径往往更有效率。


27.当一个线程进入一个对象的一个 synchronized 方法后,其它线程是否可进入此对

象的其它方法?
分几种情况:

1.其他方法前是否加了 synchronized 关键字,如果没加,则能。

2.如果这个方法内部调用了 wait,则可以进入其他 synchronized 方法。

3.如果其他个方法都加了 synchronized 关键字,并且内部没有调用 wait,则不能。


28.多线程有几种实现方法?同步有几种实现方法?

多线程有两种实现方法,分别是继承 Thread 类与实现 Runnable 接口
同步的实现方面有两种,分别是 synchronized,wait 与 notify wait():使一个线程处于等待状态,并且释放所持有的对象的 lock。
sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉 InterruptedException 异常。
notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由 JVM 确定唤
醒哪个线程,而且不是按优先级。
Allnotity():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。


29.启动一个线程是用 run()还是 start()? . 启动一个线程是调用 start()方法,使线程就绪状态,以后可以被调度为运行状态,一个线程必须关联一些具体的执行代码,run()方法

是该线程所关联的执行代码。

30. 线程的基本概念、线程的基本状态以及状态之间的关系

Java 线程在运行的生命周期中的指定时刻只可能处于下面 6 种不同状态的其中一个状态(图源
《Java 并发编程艺术》4.1.4 节)
在这里插入图片描述
线程在生命周期中并不是固定处于某一个状态而是随着代码的执行在不同状态之间切换。Java 线程
状态变迁如下图所示
在这里插入图片描述
操作系统隐藏 Java 虚拟机(JVM)中的 RUNNABLE 和 RUNNING 状态,它只能看到 RUNNABLE 状态
(图源:HowToDoInJava:Java Thread Life Cycle and Thread States),所以 Java 系统一般将
这两个状态统称为 RUNNABLE(运行中) 状态 。
操作系统隐藏 Java 虚拟机(JVM)中的 RUNNABLE 和 RUNNING 状态,它只能看到
RUNNABLE 状态,所以 Java 系统一般将这两个状态统称为 RUNNABLE(运行中) 状态 。
在这里插入图片描述
当线程执行 wait() 方法之后,线程进入 WAITING(等待)状态。进入等待状态的线程需要依靠其他线程的通
知才能够返回到运行状态,而 TIME_WAITING(超时等待) 状态相当于在等待状态的基础上增加了超时限制,比
如通过 sleep(long millis) 方法或 wait(long millis) 方法可以将 Java 线程置于 TIMED WAITING 状态。
当超时时间到达后 Java 线程将会返回到 RUNNABLE 状态。当线程调用同步方法时,在没有获取到锁的情况下,
线程将会进入到 BLOCKED(阻塞) 状态。线程在执行 Runnable 的 run()方法之后将会进入到 TERMINATED
(终止) 状态。

31.简述 synchronized 和 java.util.concurrent.locks.Lock 的异同 ?

主要相同点:Lock 能完成 synchronized 所实现的所有功能
主要不同点:Lock 有比 synchronized 更精确的线程语义和更好的性能。synchronized 会自动释放锁,而 Lock 一定要求程序员手工释放,
并且必须在 finally 从句中释放。Lock 还有更强大的功能,例如,它的 tryLock 方法可以非阻塞方式去拿锁。
举例说明(对下面的题用 lock 进行了改写):

package com.zking.interview;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ThreadTest {
private int j;
private Lock lock = new ReentrantLock();
public static void main(String[] args) {
ThreadTest tt = new ThreadTest();
for(int i=0;i<2;i++)
{ new Thread(tt.new adder()).start(); new Thread(tt.new subtractor()).start();
}
}
private class subtractor implements Runnable
{
@Override public void run() { while(true)
{
/*synchronized (ThreadTest.this) {
System.out.println("j--=" + j--)
//这里抛异常了,锁能释放吗?
}*/
lock.lock();
try
{
System.out.println("j--=" + j--);
}finally
{
lock.unlock();
}
}
}
}
private class adder implements Runnable
{
@Override public void run() { while(true)
{
/*synchronized (ThreadTest.this) {
System.out.println("j++=" + j++);
}*/
lock.lock();
try
{
System.out.println("j++=" + j++);
}finally
{
lock.unlock();
}
}
}
}
}

32.设计 4 个线程,其中两个线程每次对 j 增加 1,另外两个线程对 j 每次减少 1。写
出程序。
以下程序使用内部类实现线程,对 j 增减的时候没有考虑顺序问题。

public class
{
private int j;
public static void main(String args[]){
ThreadTest1 tt=new ThreadTest1();
Inc inc=tt.new Inc();
Dec dec=tt.new Dec();
for(int i=0;i<2;i++){
Thread t=new Thread(inc);
t.start();
t=new Thread(dec);
t.start();
}
}
private synchronized void inc(){
j++;
System.out.println(Thread.currentThread().getName()+"-inc:"+j);
}
private synchronized void dec(){
j--;
System.out.println(Thread.currentThread().getName()+"-dec:"+j);
} class Inc implements Runnable{
public void run(){
for(int i=0;i<100;i++){
inc();
}
}
}class Dec implements Runnable{
public void run(){
for(int i=0;i<100;i++){
dec();
}
}
}
}

33.ArrayList 和 LinkedList 的区别

ArrayList 和 LinkedList 在性能上各有优缺点,都有各自所适用的地方,总的说来可以描述如下:

1.对 ArrayList 和 LinkedList 而言,在列表末尾增加一个元素所花的开销都是固定的。对 ArrayList 而言,主要是在内部数组中增加一项,

指向所添加的元素,偶尔可能会导致对数组重新进行分配;而对 LinkedList 而言,这个开销是统一的,分配一个内部 Entry 对象。

2.在 ArrayList 的中间插入或删除一个元素意味着这个列表中剩余的元素都会被移动;而在 LinkedList 的中间插入或删除一个元素的开销是固定的。

3.LinkedList 不支持高效的随机元素访问。

4.ArrayList 的空间浪费主要体现在在 list 列表的结尾预留一定的容量空间,而 LinkedList 的空间花费则体现在它的每一个元素都需要消

耗相当的空间
可以这样说:当操作是在一列数据的后面添加数据而不是在前面或中间,并且需要随机地访问其中的元素时,使用 ArrayList 会提供比较好
的性能;当你的操作是在一列数据的前面或中间添加或删除数据,并且按照顺序访问其中的元素时,就应该使用 LinkedList 了


34.HashMap 和 Hashtable 的区别

(条理上还需要整理,也是先说相同点,再说不同点)
HashMap 是 Hashtable 的轻量级实现(非线程安全的实现),他们都完成了 Map 接口,主要区别在于 HashMap 允许空(null)键值(key), 由于非线程安全,在只有一个线程访问的情况下,效率要高于 Hashtable。
HashMap 允许将 null 作为一个 entry 的 key 或者 value,而 Hashtable 不允许。
HashMap 把 Hashtable 的 contains 方法去掉了,改成 containsvalue 和 containsKey。因为 contains 方法容易让人引起误解。
Hashtable 继承自 Dictionary 类,而 HashMap 是 Java1.2 引进的 Map interface 的一个实现。
最大的不同是,Hashtable 的方法是 Synchronize 的,而 HashMap 不是,在多个线程访问 Hashtable 时,不需要自己为它的方法实现同步,
而 HashMap 就必须为之提供外同步。
Hashtable 和 HashMap 采用的 hash/rehash 算法都大概一样,所以性能不会有很大的差异。
就 HashMap 与 HashTable 主要从三方面来说。
一.历史原因:Hashtable 是基于陈旧的 Dictionary 类的,HashMap 是 Java 1.2 引进的 Map 接口的一个实现
二.同步性:Hashtable 是线程安全的,也就是说是同步的,而 HashMap 是线程序不安全的,不是同步的
三.值:只有 HashMap 可以让你将空值作为一个表的条目的 key 或 value


35.List, Set, Map 是否继承自 Collection 接口?

List,Set 是,Map 不是

36.List、Map、Set 三个接口,存取元素时,各有什么特点?

List 以特定次序来持有元素,可有重复元素。Set 无法拥有重复元素,内部排序。Map 保存 key-value 值,value 可多值。
HashSet 按照 hashcode 值的某种运算方式进行存储,而不是直接按 hashCode 值的大小进行存储。例如,“abc” —> 78,“def” —> 62,“xyz”
—> 65 在 hashSet 中的存储顺序不是 62,65,78。LinkedHashSet 按插入的顺序存储,那被存储对象的 hashcode 方法还有什么作用呢?学
员想想!hashset 集合比较两个对象是否相等,首先看 hashcode 方法是否相等,然后看 equals 方法是否相等。new 两个 Student 插入到
HashSet 中,看 HashSet 的 size,实现 hashcode 和 equals 方法后再看 size。
同一个对象可以在 Vector 中加入多次。往集合里面加元素,相当于集合里用一根绳子连接到了目标对象。往 HashSet 中却加不了多次
的。


37.去掉一个 List 集合中重复的元素

public class Demo5 {
public static void main(String[] args) {
List list = new ArrayList<>();
// list.add("a");
// list.add("b");
// list.add("c");
// if(!list.contains("b")) {
// list.add("b");
// }
// System.out.println(list);
list.add(new Student("zs", 16));
list.add(new Student("wyf", 18));
list.add(new Student("hzl", 17));
if (!list.contains(new Student("wyf", 18))) {
list.add(new Student("wyf", 18));
}
for (Object object : list) {
System.out.println(object);
}
}
}class Student {
private String name;
private int age;
public String getName() { return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() { return age;
}
public void setAge(int age) {
this.age = age;
}
public Student(String name, int age) { super();
this.name = name;
this.age = age;
}
@Override public String toString() { return "Student [name=" + name + ", age=" + age + "]";
}
@Override public int hashCode() {
final int prime = 31;
int result = 1; result = prime * result + age; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result;
}
@Override public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
Student other = (Student) obj;
if (age != other.age) return false;
if (name == null) {
if (other.name != null) return false;
} else if (!name.equals(other.name)) return false; return true;
}
}


38.Collection 和 Collections 的区别。

Collection 是集合类的上级接口,继承与他的接口主要有 Set 和 List. Collections 是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。


39.Set 里的元素是不能重复的,那么用什么方法来区分重复与否呢? 是用==还是

equals()? 它们有何区别?
Set 里的元素是不能重复的,元素重复与否是使用 equals()方法进行判断的。
equals()和==方法决定引用值是否指向同一对象 equals()在类中被覆盖,为的是当两个分离的对象的内容和类型相配的话,返回真值。


40.两个对象值相同(x.equals(y) == true),但却可有不同的 hash code,这句话对

不对?
对。
如果对象要保存在 HashSet 或 HashMap 中,它们的 equals 相等,那么,它们的 hashcode 值就必须相等。
如果不是要保存在 HashSet 或 HashMap,则与 hashcode 没有什么关系了,这时候 hashcode 不等是可以的,例如 arrayList 存储的对象就
不用实现 hashcode,当然,我们没有理由不实现,通常都会去实现的。


41.java 中有几种类型的流?JDK 为每种类型的流提供了一些抽象类以供继承,请说出

他们分别是哪些类?
字节流,字符流。字节流继承于 InputStream OutputStream,字符流继承于 Reader ,Writer。在 java.io 包中还有许多其他的流,主要
是为了提高性能和使用方便。


42.什么是 java 序列化,如何实现 java 序列化?

我们有时候将一个 java 对象变成字节流的形式传出去或者从一个字节流中恢复成一个 java 对象,例如,要将 java 对象存储到硬盘或者
传送给网络上的其他计算机,这个过程我们可以自己写代码去把一个 java 对象变成某个格式的字节流再传输,但是,jre 本身就提供了这种支持,我们可以调用 OutputStream 的 writeObject 方法来做,如果要让 java 帮我们做,要被传输的对象必须实现 serializable 接口,
这样,javac 编译时就会进行特殊处理,编译的类才可以被 writeObject 方法操作,这就是所谓的序列化。需要被序列化的类必须实现
Serializable 接口,该接口是一个 mini 接口,其中没有需要实现的方法,implements Serializable 只是为了标注该对象是可被序列化的。
例如,在 web 开发中,如果对象被保存在了 Session 中,tomcat 在重启时要把 Session 对象序列化到硬盘,这个对象就必须实现 Serializable 接口。如果对象要经过分布式系统进行网络传输或通过 rmi 等远程调用,这就需要在网络上传输对象,被传输的对象就必须实现
Serializable 接口


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值