java 面试题(1-50)合集

1.简述JDK、JRE、JVM?

一、JDK
JDK(Java Development Kit) 是整个JAVA的核心,
包括了Java运行环境(Java Runtime Envirnment),
一堆Java工具(javac/java/jdb等)
和Java基础的类库(即Java API 包括rt.jar)。
JDK是java开发工具包,
基本上每个学java的人都会先在机器 上装一个JDK,
那他都包含哪几部分呢?
在目录下面有 六个文件夹、
一个src类库源码压缩包、
和其他几个声明文件。
其中,真正在运行java时起作用的
是以下四个文件夹:bin、include、lib、 jre。
有这样一个关系,JDK包含JRE,而JRE包 含JVM。
bin:最主要的是编译器(javac.exe)
include:java和JVM交互用的头文件
lib:类库
jre:java运行环境

二、JRE
JRE(Java Runtime Environment,Java运行环境)
包含JVM标准实现及Java核心类库。
JRE是Java运行环境,并不是一个开发环境,
所以没有包含任何开发工具(如编译器和调试器)
JRE是指java运行环境。
光有JVM还不能成class的 执行,
因为在解释class的时候
JVM需要调用解释所需要的类库lib。(
jre里有运行.class的java.exe)
JRE ( Java Runtime Environment ),
是运行 Java 程序必不可少的
(除非用其他一些编译环境编译成.exe可执行文件……)
JRE的 地位就象一台PC机一样,
我们写好的Win64应用程序需要操作系统帮 我们运行,
同样的,我们编写的Java程序也必须要JRE才能运行。

三、JVM
JVM(Java Virtual Machine),
即java虚拟机, java运行时的环境,
JVM是一种用于计算设备的规范,
它是一个虚构出来的计算机,
是通过在实际的计算机上仿真模拟
各种计算机功能来实现的。
针对java用户,
也就是拥有可运行的.class文件包(jar或者war)的用户。
里面主要包含了jvm和java运行时基本类库(rt.jar)。
rt.jar可以简单粗暴地理解为:
它就是java源码编译成的jar包。
Java虚拟机在执行字节码时,
把字节码解释成具体平台上的机器指令执行。
这就是Java的能够“一次编译,
到处运行”的原因。

2.JDK、JRE、JVM三者的有什么联系和区别?

1.三者联系:
JVM不能单独搞定class的执行,
解释class的时候JVM需要调用解释所需要的类库lib。
在JDK下面的的jre目录里面
有两个文件夹bin和lib,
在这里可以认为bin里的就是jvm,
lib中则是jvm工作所需要的类库,
而jvm和 lib和起来就称为jre。
JVM+Lib=JRE。
总体来说就是,
我们利用JDK(调用JAVA API)开发了属于
我们自己的JAVA程序后,
通过JDK中的编译程序(javac)
将我们的文本java文件编译成JAVA字节码,
在JRE上运行这些JAVA字节码,
JVM解析这些字节码,
映射到CPU指令集或OS的系统调用。

2.三者区别:
JDK和JRE区别:
在bin文件夹下会发现,
JDK有javac.exe而JRE里面没有,
javac指令是用来将java文件编译成class文件的,
这是开发者需要的,
而用户(只需要运行的人)是不需要的。
JDK还有jar.exe, javadoc.exe等等
用于开发的可执行指令文件。
这也证实了一个是开发环境,
一个是运行环境。
b.JRE和JVM区别:
JVM并不代表就可以执行class了,
JVM执行.class还需要JRE下的lib类库的支持,
尤其是rt.jar。

3、简述Java程序编译和运行的过程?

Java程序从源文件创建到程序运行要经过两大步骤:
1、源文件由编译器编译成字节码(ByteCode);
2、字节码由java虚拟机解释运行。
因为java程序既要编译同时
也要经过JVM的解释运行,
所以说Java被称为半解释语言

图片

第一步(编译):
创建完源文件之后,
程序先要被JVM中的java编译器
进行编译为.class文件。
java编译一个类时,
如果这个类所依赖的类还没有被编译,

编译器会自动的先编译这个所依赖的类,
然后引用。
如果java编译器在指定的目录下
找不到该类所依赖的类的 .class文件
或者 .java源文件,
就会报"Cant found sysbol"的异常错误。

编译后的字节码文件格式
主要分为两部分:
常量池和方法字节码。
常量池记录的是代码出现过的
(常量、类名、成员变量等)
以及符号引用(类引用、方法引用,成员变量引用等);
方法字节码中放的是各个方法的字节码。

第二步(运行):
java类运行的过程大概分为两个步骤:
(1)类的加载
(2)类的执行。
需要说明的一点的是:
JVM主要在程序第一次运行时主动使用类的时候,
才会立即去加载。
换言之,
JVM并不是在运行时就会把所有使用到的类
都加载到内存中,
而是用到,不得不加载的时候,
才加载进来,而且只加载一次!

4、请说出八种基础数据类型及字节大小?

1. byte                 8位

2. short               16位

3. int                 32位

4. long                 64位

5. float               32位

6. double               64位

7. boolean               1位

8. char                 16位

5.说说&和&&的区别?

&和&&都是逻辑运算符,
都是判断两边同时真则为真,否则为假;
但是&&当第一个条件不成之后,
后面的条件都不执行了,
而&则还是继续执行,
直到整个条件语句执行完为止。

6.float型float f=3.4是否正确?

答:
不正确;
精度不准确,
应该用强制类型转换,
如下所示:float f=(float)3.4

7.short s1 = 1; s1 = s1 + 1;有什么错? short s1 = 1; s1 += 1;有什么错?

short s1 = 1; s1 = s1 + 1; s1+1
运算结果是int型,
需要强制转换类型;

short s1 = 1; s1 += 1;
可以正确编译,自动类型提升。

8.int 和 Integer 有什么区别?

Java 提供两种不同的类型:
引用类型和原始类型(或内置类型);
int是java的原始数据类型,
Integer是java为int提供的封装类。

引用类型和原始类型的行为完全不同,
并且它们具有不同的语义。
引用类型和原始类型具有不同的特征和用法,

它们包括:大小和速度问题,
这种类型以哪种类型的数据结构存储,

当引用类型和原始类型
用作某个类的实例数据时所指定的缺省值。

对象引用实例变量的缺省值为 null,
而原始类型实例变量的缺省值
与它们的类型有关。

9.在JAVA中,如何跳出当前的多重嵌套循环?

在最外层循环前加label标识,
然后用break:label方法即可跳出多重循环。
ok:while(true){
  while(true){
      break ok;
  }
}

10.使用嵌套的for循环打印九九乘法表。

public class test {
  public static void main(String[] args) {
      for (int i = 1; i <= 9; i++) {
          for (int j = 1; j <= i; j++) {
              System.out.print(j + "*" + i + "=" + i * j + " ");
          }
          System.out.println();
      }
  }

}

11、面向对象的特征有哪些方面

1.抽象:
抽象就是忽略一个主题中与当前目标无关的那些方面,
以便更充分地注意与当前目标有关的方面。
抽象并不打算了解全部问题,而只是选择其中的一部分,
暂时不用部分细节。
抽象包括两个方面,
一是过程抽象,
二是数据抽象。

2.继承:
继承是一种联结类的层次模型,
并且允许和鼓励类的重用,
它提供了一种明确表述共性的方法。
对象的一个新类可以从现有的类中派生,
这个过程称为类继承。
新类继承了原始类的特性,
新类称为原始类的派生类(子类),
而原始类称为新类的基类(父类)。
派生类可以从它的基类那里继承方法和实例变量,
并且类可以修改或增加新的方法使之更适合特殊的需要。

3.封装:
封装是把过程和数据包围起来,
对数据的访问只能通过已定义的界面。
面向对象计算始于这个基本概念,
即现实世界可以被描绘成一系列完全自治、
封装的对象,
这些对象通过一个受保护的接口访问其他对象。
4. 多态性:
多态性是指允许不同类的对象对同一消息作出响应。
多态性包括参数化多态性和包含多态性。
多态性语言具有灵活、抽象、行为共享、代码共享的优势,
很好的解决了应用程序函数同名问题。

12.java 创建对象的几种方式

采用new

通过反射

采用clone

通过序列化机制

前2者都需要显式地调用构造方法。
造成耦合性最高的恰好是第一种,
因此你发现无论什么框架,
只要涉及到解耦必先减少new的使用。

13.修饰符public,private,protected,以及不写时的区别

修饰符当前类同一package子孙类其他package
public
protected×
default××
private×××

不写时默认为friendly/default friendly权限是java的默认权限,也称作包(package)访问权限 只要不加private、public、protect的 就是friendly访问权限, 所有的成员仅限同一个包内的成员访问

14.String s = new String("xyz");创建了几个String Object

两个,一个字符对象,一个字符对象引用对象

15.Math.round(11.5)等於多少? Math.round(-11.5)等於多少?

Math.round(11.5)==12;
Math.round(-11.5)==-11;
round方法返回与参数最接近的长整数,
参数加1/2后求其floor

16.Java有没有goto?

java中的保留字,现在没有在java中使用

17.Overload和Override的区别。Overloaded的方法是否可以改变返回值的类型

方法的重写Overriding和重载Overloading是Java多态性的不同表现。
重写Overriding是父类与子类之间多态性的一种表现,
重载Overloading是一个类中多态性的一种表现。
如果在子类中定义某方法与其父类有相同的名称和参数,
我们说该方法被重写 (Overriding)。
子类的对象使用这个方法时,
将调用子类中的定义,
对它而言,
父类中的定义如同被"屏蔽"了。
如果在一个类中定义了多个同名的方法,
它们或有不同的参数个数或有不同的参数类型,
则称为方法的重载(Overloading)。
Overloaded的方法是可以改变返回值的类型

18.abstract class和interface有什么区别

声明方法的存在而不去实现它的类被叫做抽象类(abstract class),
它用于要创建一个体现某些基本行为的类,
并为该类声明方法,
但不能在该类中实现该类的情况。
不能创建abstract 类的实例。

然而可以创建一个变量,
其类型是一个抽象类,
并让它指向具体子类的一个实例。
不能有抽象构造函数或抽象静态方法。
Abstract 类的子类为它们父类中的
所有抽象方法提供实现,
否则它们也是抽象类为。

取而代之,在子类中实现该方法。
知道其行为的其它类可以在类中实现这些方法
接口(interface)是抽象类的变体。

在接口中,所有方法都是抽象的。
多继承性可通过实现这样的接口而获得。
接口中的所有方法都是抽象的,
没有一个有程序体。

接口只可以定义static final成员变量。
接口的实现与子类相似,
除了该实现类不能从接口定义中继承行为。

当类实现特殊接口时,
它定义(即将程序体给予)所有这种接口的方法。
然后,它可以在实现了该接口的类的
任何对象上调用接口的方法。

由于有抽象类,
它允许使用接口名作为引用变量的类型。
通常的动态联编将生效。
引用可以转换到接口类型或从接口类型转换,
instanceof 运算符可以用来
决定某对象的类是否实现了接口

19.接口是否可继承接口?

接口可以继承接口。
抽象类可以实现(implements)接口,
抽象类是否可继承实体类,但
前提是实体类必须有明确的构造函数

20.swtich是否能作用在byte上,是否能作用在long上,是否能作用在String上

swtich()里面必须是int和enum--即枚举类型。
short、 char 或者 byte他会自动转换为int的。。
long不能自动转换为int..
因为long比int范围大..可能会丢失精度..

在java的1.7之后的jdk版本,
java中的switch里面表达式的类型可以是string类型,
之前是不可以使用的

21.final, finally, finalize的区别

1、final修饰符(关键字)。
被final修饰的类,
就意味着不能再派生出新的子类,
不能作为父类而被子类继承。
因此一个类不能既被abstract声明,
又被final声明。将变量或方法声明为final,
可以保证他们在使用的过程中不被修改。
被声明为final的变量必须在声明时给出变量的初始值,
而在以后的引用中只能读取。
被final声明的方法也同样只能使用,
即不能方法重写。

22、finally是在异常处理时

提供finally块来执行任何清除操作。
不管有没有异常被抛出、捕获,finally块都会被执行。
try块中的内容是在无异常时执行到结束。
catch块中的内容,
是在try块内容发生catch所声明的异常时,
跳转到catch块中执行。
finally块则是无论异常是否发生,
都会执行finally块的内容,
所以在代码逻辑中有需要
无论发生什么都必须执行的代码,
就可以放在finally块中。

3、finalize是方法名。
java技术允许使用finalize()方法
在垃圾收集器将对象从内存中
清除出去之前做必要的清理工作。
这个方法是由垃圾收集器
在确定这个对象没有被引用时对这个对象调用的。
它是在object类中定义的,
因此所有的类都继承了它。
子类覆盖finalize()方法以整理系统资源或者被执行其他清理工作。
finalize()方法是在垃圾收集器
删除对象之前对这个对象调用的。

###2.Overload和Override的区别。
首先重载和重写是应用于两个不同场景下面的两种不同的手段:
两者各自的特征:
重载(Overload):首先是位于一个类之中或者其子类中,
具有相同的方法名,
但是方法的参数不同,
返回值类型可以相同也可以不同。
(1):方法名必须相同
(2):方法的参数列表一定不一样。
(3):访问修饰符和返回值类型可以相同也可以不同。
其实简单而言:重载就是对于不同的情况写不同的方法。
比如,同一个类中,
写不同的构造函数用于初始化不同的参数。

重写(override):一般都是表示子类和父类之间的关系,
其主要的特征是:
方法名相同,
参数相同,
但是具体的实现不同。

重写的特征:
(1):方法名必须相同,返回值类型必须相同
(2):参数列表必须相同
(3):访问权限不能比父类中被重写的方法的访问权限更低。
例如:如果父类的一个方法被声明为public,
那么在子类中重写该方法就不能声明为protected。
(4):子类和父类在同一个包中,
那么子类可以重写父类所有方法,
除了声明为private和final的方法。
(5):构造方法不能被重写,
简单而言:就是具体的实现类对于父类的该方法实现不满意,
需要自己在写一个满足于自己要求的方法。

23. Java中的String,StringBuilder,StringBuffer三者的区别?

首先说运行速度,或者说是执行速度,
在这方面运行速度快慢为:StringBuilder > StringBuffer > String
String最慢的原因:
String为字符串常量,
而StringBuilder和StringBuffer均为字符串变量,
即String对象一旦创建之后该对象是不可更改的,
但后两者的对象是变量,是可以更改的。
而StringBuilder和StringBuffer的对象是变量,
对变量进行操作就是直接对该对象进行更改,
而不进行创建和回收的操作,
所以速度要比String快很多。

String               ---->     字符串常量
StringBuffer     ---->     字符串变量(线程安全的)
StringBuilder   ---->     字符串变量(非线程安全的)

String:适用于少量的字符串操作的情况
StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况
StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况

24.GC是什么? 为什么要有GC?

GC是垃圾收集的意思,
内存处理是编程人员容易出现问题的地方,
忘记或者错误的内存回收
会导致程序或系统的不稳定甚至崩溃,
Java提供的GC功能
可以自动监测对象是否超过作用域
从而达到自动回收内存的目的,

Java语言没有提供释放已分配内存的显示操作方法。J
ava程序员不用担心内存管理,
因为垃圾收集器会自动进行管理。
要请求垃圾收集,
可以调用下面的方法之一:
System.gc()或Runtime.getRuntime().gc(),
但JVM可以屏蔽掉显示的垃圾回收调用。

垃圾回收可以有效的防止内存泄露,
有效的使用可以使用的内存。
垃圾回收器通常是作为一个单独的低优先级的线程运行,
不可预知的情况下对内存堆中
已经死亡的或者长时间没有使用的对象进行清除和回收,
程序员不能实时的调用垃圾回收器
对某个对象或所有对象进行垃圾回收。

Java有了GC,
就不需要程序员去人工释放内存空间。
当Java虚拟机发觉内存资源紧张的时候,
就会自动地去清理无用变量所占用的内存空间。
当然,如果需要,
程序员可以在Java程序中显式地使用System.gc()
来强制进行一次立即的内存清理。

25.构造器如何工作?

Java在构造实例时的顺序是这样的:
1、分配对象空间,并将对象中成员初始化为0或者空
,java不允许用户操纵一个不定值的对象。  
2、执行属性值的显式初始化   
3、执行构造器   
4 、将变量关联到堆中的对象上

而执行构造器的步骤有可以分为以下几步:

1、Bind构造器的参数
2、如果显式的调用了this,那就递归调用this构造器然后跳到步骤5
3、递归调用显式或者隐式的父类构造器,除了Object以外,因为它没有父类
4、执行显式的实例变量初始化(也就是上边的流程中的第二步,调用返回以后执行,
  这个步骤相当于在父构造器执行后隐含执行的,看样子像一个特殊处理)

26.构造器Constructor是否可被override?

构造器Constructor不能被继承,
因此不能重写Overriding,
但可以被重载Overloading
1). 构造器不能是native,final,static,synchronized 的,
可以是public,private,或什么都没有。
2). 构造器函数里可以写return呢,但后面什么都不许有(包括null)
3). 构造器不能返回值.
但如果有个"构造器"返值了,
它就不是构造器喽,只是个普通方法
4). super();this();这两个方法只能在构造方法里调用.
5). 成员变量声明时候赋值,比构造函数还早.

27.写一个Singleton出来。

Singleton模式主要作用是保证在Java应用程序中,
一个类Class只有一个实例存在。
一般Singleton模式通常有几种种形式:
第一种形式: 定义一个类,
它的构造函数为private的,
它有一个static的private的该类变量,
在类初始化时实例话,
通过一个public的getInstance方法获取对它的引用,
继而调用其中的方法。

public class Singleton {
  private Singleton(){}
  //在自己内部定义自己一个实例,是不是很奇怪?
  //注意这是private 只供内部调用
  private static Singleton instance = new Singleton();
  //这里提供了一个供外部访问本class的静态方法,可以直接访问  
  public static Singleton getInstance() {
    return instance;   
   }
}

第二种形式:
public class Singleton {
  private static Singleton instance = null;
  public static synchronized Singleton getInstance() {
  //这个方法比上面有所改进,不用每次都进行生成对象,只是第一次     
  //使用时生成实例,提高了效率!
  if (instance==null)
    instance=new Singleton();
return instance;   }
}

其他形式:
定义一个类,
它的构造函数为private的,
所有方法为static的。
一般认为第一种形式要更加安全些

28.error和exception有什么区别?

Error类和Exception类都继承自Throwable类。
二者的不同之处:
Exception:
1.可以是可被控制(checked) 或不可控制的(unchecked)。
2.表示一个由程序员导致的错误。
3.应该在应用程序级被处理。

Error:
1.总是不可控制的(unchecked)。
2.经常用来用于表示系统错误或低层资源的错误。
3.如何可能的话,应该在系统级被捕捉。

error 表示恢复不是不可能但很困难的情况下的一种严重问题。
比如说内存溢出。
不可能指望程序能处理这样的情况。

exception 表示一种设计或实现问题。
也就是说,
它表示如果程序运行正常,
从不会发生的情况。

29.HashMap和Hashtable的区别?

hashmap:
1.线程不安全
2.允许有null的键和值
3.效率高一点、
4.方法不是Synchronize的要提供外同步
5.有containsvalue和containsKey方法
6.HashMap 是Java1.2 引进的Map interface 的一个实现
7.HashMap是Hashtable的轻量级实现

hashtable:
1.线程安全
2.不允许有null的键和值
3.效率稍低、
4.方法是是Synchronize的
5.有contains方法方法
6.Hashtable 继承于Dictionary 类
7.Hashtable 比HashMap 要旧

30.==和equals()区别?

对于==,
如果作用于基本数据类型的变量,
则直接比较其存储的
“值”是否相等;
如果作用于引用类型的变量,
则比较的是所指向的对象的地址

对于equals方法,
注意:equals方法不能作用于基本数据类型的变量
如果没有对equals方法进行重写,
则比较的是引用类型的变量所指向的对象的地址;
诸如String、Date等类对equals方法进行了重写的话,
比较的是所指向的对象的内容。

31.静态变量和实例变量的区别?

静态变量也叫类变量,
这种变量前加了static修饰符。
可以直接用类名调用,
也可以用对象调用,
而且所有对象的同一个类变量
都是共享同一块内存空间。

实例变量也叫对象变量,
这种变量没有加static修饰符。
只能通过对象调用,
而且所有对象的同一个实例变量
是共享不同的内存空间的。

区别在于:
静态变量是所有对象共有的,
某一个对象将它的值改变了,
其他对象再去获取它的值,
得到的是改变后的值;
实例变量则是每一个对象私有的,
某一个对象将它的值改变了,
不影响其他对象取值的结果,
其他对象仍会得到实例变量
一开始就被赋予的值。

实例变量必须创建对象后
才可以通过这个对象来使用,
静态变量
则可以直接使用类名来引用。

32.垃圾回收器的基本原理是什么?

垃圾回收器是Java平台中用的
最频繁的一种对象销毁方法。
垃圾回收器会全程侦测Java应用程序的运行情况。
当发现有些对象成为垃圾时,
垃圾回收器就会销毁这些对象,
并释放这些对象所占用的内存空间。
在这里,程序开发人员需要知道,
在哪些情况下垃圾回收器
会认为这些对象是垃圾对象。
通常情况下,如果发生以下两种情况时,
系统会认为这些对象是垃圾对象,
需要销毁。
一是将一个NULL值赋值给对象。
二是对象其超出了作用范围,

33.垃圾回收器可以马上回收内存吗?

不会马上回收,
只有在必须回收时才会回收,
或者你可以调用垃圾回收方法,
虚拟机会在空闲时回收,
至于什么时候回收,
虚拟机说了算

34.有什么办法主动通知虚拟机进行垃圾回收?

对于GC来说,
当程序员创建对象时,
GC就开始监控这个对象的地址、
大小以及使用情况。
通常,GC采用有向图的方式记录和管理堆(heap)中的所有对象。
通过这种方式确定哪些对象是”可达的”,
哪些对象是”不可达的”。
当GC确定一些对象为”不可达”时,
GC就有责任回收这些内存空间。
可以。程序员可以手动执行System.gc(),
通知GC运行,
但是Java语言规范
并不保证GC一定会执行。

System.gc()的工作原理
Java中的内存分配
是随着new一个新的对象来实现的,
这个很简单,
而且也还是有一些
可以“改进”内存回收的机制的,
其中最显眼的
就是这个System.gc()函数。

乍一看这个函数似乎是可以进行垃圾回收的,
可事实并不是那么简单。
其实这个gc()函数的作用只是提醒虚拟机:
程序员希望进行一次垃圾回收。
但是它不能保证垃圾回收一定会进行,
而且具体什么时候进行
是取决于具体的虚拟机的,
不同的虚拟机有不同的对策。

35.内部类可以引用他包含类的成员吗?

完全可以。
如果不是静态内部类,
那没有什么限制!
一个内部类对象可以访问
创建它的外部类对象的成员包括私有成员。
如果你把静态嵌套类当作内部类的一种特例,
那在这种情况下不可以访问外部类的
普通成员变量,
而只能访问外部类中的静态成员。

内部类的访问规则:
1、内部类可以直接访问外部类中的成员,
包括私有。
之所以可以直接访问外部类中的成员,
是因为内部类中持有了
一个外部类的引用,
格式 外部类名.this
2、外部类要访问内部类,必须建立内部类对象。

内部类定义在局部时,
1、不可以被成员修饰符修饰
2、可以直接访问外部类中的成员,
因为还持有外部类中的引用。
但是不可以访问它所在的局部中的变量。
只能访问被final修饰的局部变量。

36.Java 中的异常处理机制的简单原理和应用?

一、Execption可以分为
java标准定义的异常
程序员自定义异常2种
1.一种是当程序违反了java语规则的时候,
JAVA虚拟机就会将发生的错误
表示为一个异常.
这里语法规则指的是
JAVA类库内置的语义检查。
例如 int i = 2 / 0
或者 String str = null;str.length();

2.另一种情况就是JAVA允许程序员
扩展这种语义检查,
程序员可以创建自己的异常,
并自由选择在何时用throw关键字
引发异常。

例如 Exception ex = new Exception("这是我自定义的异常;
throw ex;

所有的异常都是Thowable的子类。
异常处理是与程序执行是并行的。

二、异常的处理方式
1.捕获异常

try {
int i = 2 / 0;
} catch (Exception ex) {
  ex.printStackTrace();
  System.out.println("异常信息:" + ex.getMessage());
}

2.上抛异常 throws
public void test() throws Exception {
  String str = null;
  str.length();
}

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

(1)运行时异常
都是RuntimeException类
及其子类异常,
如NullPointerException、
IndexOutOfBoundsException等,
这些异常是不检查异常,
程序中可以选择捕获处理,
也可以不处理。
这些异常一般是由程序逻辑错误引起的,
程序应该从逻辑角度
尽可能避免这类异常的发生。

当出现RuntimeException的时候,
我们可以不处理。
当出现这样的异常时,
总是由虚拟机接管。
比如:我们从来没有人
去处理过NullPointerException异常,
它就是运行时异常,
并且这种异常还是最常见的异常之一。

出现运行时异常后,
系统会把异常一直往上层抛,
一直遇到处理代码。
如果没有处理块,
到最上层,
如果是多线程就由Thread.run()抛出,
如果是单线程就被main()抛出。
抛出之后,
如果是线程,
这个线程也就退出了。
如果是主程序抛出的异常,
那么这整个程序也就退出了。
运行时异常是Exception的子类,
也有一般异常的特点,
是可以被Catch块处理的。
只不过往往我们不对他处理罢了。
也就是说,
你如果不对运行时异常进行处理,
那么出现运行时异常之后,
要么是线程中止,
要么是主程序终止。

如果不想终止,
则必须扑捉所有的运行时异常,
决不让这个处理线程退出。
队列里面出现异常数据了,
正常的处理应该是把异常数据舍弃,
然后记录日志。
不应该由于异常数据
而影响下面对正常数据的处理。

(2)非运行时异常
是RuntimeException以外的异常,
类型上都属于Exception类及其子类。
如 IOException、SQLException 等
以及用户自定义的Exception异常。
对于这种异常,
JAVA编译器强制要求我们
必需对出现的这些异常进行catch并处理,
否则程序就不能编译通过。
所以,面对这种异常不管我们是否愿意,
只能自己去写一大堆catch块
去处理可能的异常。

38.为什么Map接口不继承Collection 接口?

Collection是最基本的集合接口,
声明了适用于JAVA集合(只包括Set和List)
的通用方法。
Set 和List 都继承了Conllection;
Set具有与Collection完全一样的接口,
因此没有任何额外的功能,
不像前面有两个不同的List。
实际上Set就是Collection,只 是行为不同。
(这是继承与多态思想的典型应用:表现不同的行为。)
Set不保存重复的元素(至于如何判断元素相同则较为负责)

Map没有继承于Collection接口
从Map集合中检索元素时,
只要给出键对象,
就会返回对应的值对象。


Collection 和 Map 的区别
容器内每个为之所存储的元素个数不同。
Collection类型者,
每个位置只有一个元素。
Map类型者,
持有 key-value pair,
像个小型数据库

尽管Map接口和它的实现也是集合框架的一部分,
但Map不是集合,
集合也不是Map。
因此,Map继承Collection毫无意义,
反之亦然。
如果Map继承Collection接口,
那么元素去哪儿?
Map包含key-value对,
它提供抽取key或value列表集合的方法,
但是它不适合“一组对象”规范。

40.comparable 和 comparator的不同之处?

Comparable可以认为是一个内比较器,
实现了Comparable接口的类有一个特点,
就是这些类是可以和自己比较的,
至于具体和另一个实现了Comparable接口的类如何比较,
则依赖compareTo方法的实现,
compareTo方法也被称为自然比较方法。
如果开发者add进入
一个Collection的对象想要Collections的sort方法
帮你自动进行排序的话,
那么这个对象必须实现Comparable接口。
compareTo方法的返回值是int,
有三种情况:
1、比较者大于被比较者
(也就是compareTo方法里面的对象),
那么返回正整数
2、比较者等于被比较者,那么返回0
3、比较者小于被比较者,那么返回负整数


Comparator可以认为是是一个外比较器,
个人认为有两种情况
可以使用实现Comparator接口的方式:
1、一个对象不支持自己和自己比较
(没有实现Comparable接口),
但是又想对两个对象进行比较
2、一个对象实现了Comparable接口,
但是开发者认为compareTo方法中的
比较方式并不是自己想要的那种比较方式

Comparator接口里面有一个compare方法,
方法有两个参数T o1和T o2,
是泛型的表示方式,
分别表示待比较的两个对象,
方法返回值和Comparable接口一样是int,
有三种情况:
1、o1大于o2,返回正整数
2、o1等于o2,返回0
3、o1小于o2,返回负整数

总结
两种比较器Comparable和Comparator,
后者相比前者有如下优点:
1、如果实现类没有实现Comparable接口,
又想对两个类进行比较
或者实现类实现了Comparable接口,
但是对compareTo方法内的比较算法不满意,
那么可以实现Comparator接口,
自定义一个比较器,
写比较算法

2、实现Comparable接口的方式比
实现Comparator接口的耦合性要强一些,
如果要修改比较算法,
要修改Comparable接口的实现类,
而实现Comparator的类是在外部进行比较的,
不需要对实现类有任何修 改。
从这个角度说,
其实有些不太好,
尤其在我们将实现类的.class文件
打成一个.jar文件
提供给开发者使用的时候。
实际上实现Comparator 接口的方式
后面会写到就是一种典型的策略模式。

当然,这不是鼓励用Comparator,
意思是开发者还是要在具体场景下
选择最合适的那种比较器而已。

41.Iterator、ListIterator 和 Enumeration的区别?

迭代器是一种设计模式,
它是一个对象,
它可以遍历并选择序列中的对象,
而开发人员不需要了解
该序列的底层结构。
迭代器通常被称为“轻量级”对象,
因为创建它的代价小。
Java中的Iterator功能比较简单,
并且只能单向移动:
(1) 使用方法iterator()要求容器返回一个Iterator。
第一次调用Iterator的next()方法时,
它返回序列的第一个元素。
注意:iterator()方法是java.lang.Iterable接口
被Collection继承。
(2) 使用next()获得序列中的下一个元素。
(3) 使用hasNext()检查序列中是否还有元素。
(4) 使用remove()将迭代器新返回的元素删除。
Iterator是Java迭代器最简单的实现,
为List设计的ListIterator具有更多的功能,
它可以从两个方向遍历List,
也可以从List中插入和删除元素。
-------------
ListIterator的特点:
它的父类接口是Iterator,
名称是系列表迭代器,
允许程序员按任一方向遍历列表、
迭代期间修改列表,
并获得迭代器在列表中的当前位置。
ListIterator没有当前元素,
它的光标位置始终位于调用previous()
所返回的元素和调用next()
所返回的元素之间。
长度为n的列表的迭代器有n+1个
可能的指针位置。
------------------
Enumeration的特点:
API中是这样描述的,
它主要是和Vector结合配套使用。
另外此接口的功能与Iterator接口的功能是重复的,
此外,Iterator接口添加了
一个可选的移除操作,
并且使用较短的方法名。
新的实现应该优先
考虑使用Iterator接口
而不是Enumeration接口。
-----------------------
java中的集合类都提供了
返回Iterator的方法,
就是迭代器,
它和Enumeration的主要区别
其实就是Iterator可以删除元素,
但是Enumration却不能。

42.Java 中 Set 与 List 有什么不同?

1、Set 不允许重复,List允许重复
2、Set 没有顺序,List有顺序

List接口对Collection进行了简单的扩充,
它的具体实现类常用的有ArrayList和LinkedList。
你可以将任何东西放到一个List容器中,
并在需要时从中取出。
ArrayList从其命名中可以看出
它是一种类似数组的形式进行存储,
因此它的随机访问速度极快,
而LinkedList的内部实现是链表,
它适合于在链表中间
需要频繁进行插入和删除操作。
在具体应用时可以根据需要自由选择。
前面说的Iterator只能对容器进行向前遍历,
而ListIterator则继承了Iterator的思想,
并提供了对List进行双向遍历的方法。

Set接口也是Collection的一种扩展,
而与List不同的时,
在Set中的对象元素不能重复,
也就是说你不能把同样的东西
两次放入同一个Set容器中。
它的常用具体实现有HashSet和TreeSet类。
HashSet能快速定位一个元素,
但是你放到HashSet中的对象
需要实现hashCode()方法,
它使用了前面说过的哈希码的算法。
而TreeSet则将放入
其中的元素按序存放,
这就要求你放入
其中的对象是可排序的,
这就用到了集合框架提供的
另外两个实用类Comparable和Comparator。
一个类是可排序的,
它就应该实现Comparable接口。
有时多个类具有相同的排序算法,
那就不需要在每分别重复定义
相同的排序算法,
只要实现Comparator接口即可。
集合框架中还有两个很实用的公用类:
Collections和Arrays。
Collections提供了对一个Collection容器
进行诸如排序、复制、查找和填充等
一些非常有用的方法,
Arrays则是对一个数组进行类似的操作。

43.arraylist 与 vector 的区别?

ArrayList与Vector的区别,
这主要包括两个方面:.
1.同步性:
Vector是线程安全的,
也就是说是它的方法之间是线程同步的,
而ArrayList是线程序不安全的,
它的方法之间是线程不同步的。
如果只有一个线程会访问到集合,
那最好是使用ArrayList,
因为它不考虑线程安全,
效率会高些;
如果有多个线程会访问到集合,
那最好是使用Vector,
因为不需要我们自己
再去考虑和编写线程安全的代码。

备注:
对于Vector&ArrayList、Hashtable&HashMap,
要记住线程安全的问题,
记住Vector与Hashtable是旧的,
是java一诞生就提供了的,
它们是线程安全的,
ArrayList与HashMap是java2时才提供的,
它们是线程不安全的。

2.数据增长:
ArrayList与Vector
都有一个初始的容量大小,
当存储进它们里面的元素的个数超过了容量时,
就需要增加ArrayList与Vector的存储空间,
每次要增加存储空间时,
不是只增加一个存储单元,
而是增加多个存储单元,
每次增加的存储单元的个数
在内存空间利用与程序效率之间
要取得一定的平衡。
Vector默认增长为原来两倍,
而ArrayList的增长策略
在文档中没有明确规定
从源代码看到的是增长为原来的1.5倍。
ArrayList与Vector都可以设置初始的空间大小,
Vector还可以设置增长的空间大小,
而ArrayList没有提供
设置增长空间的方法。
总结:
即Vector增长原来的一倍,
ArrayList增加原来的0.5倍。

44.什么类实现了List接口?

List接口的实现类中
最经常使用最重要的就是这三个:
ArrayList、
Vector、
LinkedList。

1.三个都直接实现了AbstractList这个抽象类
2,ArrayList和Vector都实现了
RandomAccess接口。
而LinkedList没有。
这是什么意思呢?             
在JDK 中,
RandomAccess接口是一个空接口,
所以它没有实际意义。就是一个标记,
标记这个类支持高速随机訪问,
所以,arrayList和 vector是支持随机訪问的,
可是LinkedList不支持持        
3.serializbale接口表明他们都支持序列化。

45.什么类实现了Set接口?

HashSet
LinkedHashSet
TreeSet

HashSet是使用哈希表(hash table)实现的,
其中的元素是无序的。
HashSet的
add、
remove、
contains方法
的时间复杂度为常量O(1)。
--------------------
TreeSet使用树形结构
算法书中的红黑树red-black tree
实现的。
TreeSet中的元素是可排序的,
但add、remove和contains方法的时间
复杂度为O(log(n))。
TreeSet还提供了
first()、
last()、
headSet()、
tailSet()等
方法来操作排序后的集合。
-----------------------
LinkedHashSet介于HashSet和TreeSet之间。
它基于一个由链表实现的哈希表,
保留了元素插入顺序。
LinkedHashSet中基本方法的
时间复杂度为O(1)。

46.如何保证一个集合线程安全?

Java提供了不同层面的线程安全支持。
在传统集合框架内部,
除了Hashtable等同步容器,
还提供了所谓的同步包装器(Synchronized Wrapper),
可以调用Collections工具类提供的包装方法,
来获取一个同步的包装容器,
例如Collections.synchronizedMap()。
但是它们都是利用非常粗粒度的同步方式,
在高并发情况下的性能比较低下。
另外,更加普遍的选择是利用并发包(java.util.concurrent)
提供的线程安全容器类:
各种并发容器,
比如ConcurrentHashMap、
CopyOnWriteArrayList。
各种线程安全队列(Queue/Deque),
比如ArrayBlockingQueue、
SynchronousQueue。
各种有序容器的线程安全版本等。
具体保证线程安全的方式,
包括有从简单的synchronized方式,
到基于更加精细化的,
比如基于分离锁实现的ConcurrentHashMap等并发实现等。
具体选择要看开发的场景需求,
总体来说,
并发包内提供的容器通用场景,
远远优于早期的简单同步实现。

为什么需要ConcurrentHashMap
首先,Hashtable本身比较低效,
因为它的实现基本就是
将put、get、size等方法
简单粗暴地加上“synchronized”。
这就导致了所有并发操作都要竞争同一把锁,
一个线程在进行同步操作时,
其它线程只能等待,
大大降低了并发操作的性能。

47.是否可以往 TreeSet 或者 HashSet 中添加 null 元素?

1.TreeSet 是二差树实现的,
Treeset中的数据是自动排好序的,
不允许放入null值

2.HashSet 是哈希表实现的,
HashSet中的数据是无序的,
可以放入null,
但只能放入一个null,
两者中的值都不能重复,
就如数据库中唯一约束

3.HashSet要求放入的对象
必须实现HashCode()方法,
放入的对象,是以hashcode码作为标识的,
而具有相同内容的String对象,
hashcode是一样,
所以放入的内容不能重复。
但是同一个类的对象可以放入不同的实例

48.hashCode() 和 equals() 方法的重要性?如何在Java中使用它们?

Java中的HashMap使用
hashCode()和equals()方法
来确定键值对的索引,
当根据键获取值的时候
也会用到这两个方法。
如果没有正确的实现这两个方法,
两个不同的键可能会有相同的hash值,
因此可能会被集合认为是相等的。
而且,这两个方法也用来发现重复元素,
所以这两个方法的实现对HashMap的
精确性和正确性是至关重要的。

同一个对象(没有发生过修改)
无论何时调用hashCode(),
得到的返回值必须一样。
hashCode()返回值相等,
对象不一定相等,
通过hashCode()和equals()
必须能唯一确定一个对象。
一旦重写了equals(),
就必须重写hashCode()。
而且hashCode()生成哈希值的依据应该是
equals()中用来比较是否相等的字段。
如果两个由equals()规定相等的对象
生成的hashCode不等,
对于HashMap来说,
他们可能分别映射到不同位置,
没有调用equals()比较是否相等的机会,
两个实际上相等的对象可能被插入到不同位置,
出现错误。
其他一些基于哈希方法的集合类
可能也会有这个问题。
----------------
怎么判断两个对象是相同的?
使用等号== 判断两个对象是否相同,
这种是严格的相同,
即内存中的同一个对象
Object的equal方法就是使用==判断两个对象是否相同
------------
集合set要求元素是唯一的,怎么实现?
要实现元素的唯一,
需要在往集合set中添加元素时,
判断集合set是否存在相同的元素,
如果存在,则不添加,反之。
那么怎么确定两个元素是否相同,
1.如果是使用等号==判断两个元素是否相同,
即默认使用Object的equals的方法。
2.如果没有使用等号==判断两个元素是否相同,
而是按照某种业务规则判断两个元素是否相同,
即重写了Object的equals的方法。
----------------------
当重写equals方法,必须重写hashCode方法吗?
不是必须的,
得看具体的情况
当equals方法返回的结果和使用等号
比较的结果是一致的时候,
是没有必要重写hashCode方法。
当用等号比较对象,
只有是内存中同一个对象实例,
才会返回true,
当然调用其hashCode()方法
肯定返回相同的值,
这满足了满足了hashCode的约束条件,
所以不用重写hashCode()方法。

当equals方法返回的结果
和使用等号比较的结果是不一致的时候,
就需要重写hashCode方法。
当重写后的equals方法
不认为只有是在内存中同一个对象实例,
才返回true,
如果不重新hashCode方法()
Object的hashCode()方法 是对内存地址的映射,
hashCode方法返回的值肯定是不同的,
这违背了hashCode的约束条件,
所以必须要重新hashCode方法,
并满足对hashCode的约束条件。

49.array 和 arraylist 的区别?

两者间的区别:
Array 的容量是固定的,
ArrayList 的容量是根据需求自动扩展
ArrayList 提供了 添加、插入或移除
某一范围元素的方法
而 Array 中,
只能一次获取或设置一个元素值
用Synchronized方法可以
很容易地创建ArrayList的同步版本
而 Array 将一直保持
它知道用户实现同步为止

array 数组的用法
type [] name = new type [size];
注意:size不能省略,type前后要一致
缺点:在数据间插入数据是


ArrayList 动态数组的用法
是 Array 的复杂版本
动态的增加和减少元素,
实现 ICollection 和 IList 接口
灵活的设置数组大小

50.如何将一个字符串转换为arraylist?

string 转 ArrayList
先将字符串按照某个字符切割,转为string数组
然后用Arrays的asList方法,将数组转为List
public class test1 {
  public static void main(String[] args) {
      //string 转 ArrayList
      String str1 = "a,b,c";
      ArrayList<String> list =
      new ArrayList<String>(Arrays.asList(str1.split(",")));
      System.out.println(list);
  }
}

ArrayList 转 string
public class test1 {
  public static void main(String[] args) {
      //ArrayList 转 string
      ArrayList<String> list = new ArrayList<String>();

      list.add("a");
      list.add("b");
      list.add("c");

      System.out.println(list);//[a, b, c]

      String list_str = StringUtils.join(list,",");

      System.out.println(list_str);//a,b,c
  }
}

50.如何将一个字符串转换为arraylist?

string 转 ArrayList
先将字符串按照某个字符切割,转为string数组
然后用Arrays的asList方法,将数组转为List
public class test1 {
  public static void main(String[] args) {
      //string 转 ArrayList
      String str1 = "a,b,c";
      ArrayList<String> list =
      new ArrayList<String>(Arrays.asList(str1.split(",")));
      System.out.println(list);
  }
}

ArrayList 转 string
public class test1 {
  public static void main(String[] args) {
      //ArrayList 转 string
      ArrayList<String> list = new ArrayList<String>();

      list.add("a");
      list.add("b");
      list.add("c");

      System.out.println(list);//[a, b, c]

      String list_str = StringUtils.join(list,",");

      System.out.println(list_str);//a,b,c
  }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值