Java面试题整理(汇编)

Java面试题

1.Java语言是由()语言改进并重新设计而来的。

A.Ada    			B.C++			C.Pascal			D.BASIC

答案:B。
Ada语言是美国军方为了整合不同语言开发的系统而发明的一种语言,其最大的特点是实时性,在Ada95中已加入面向对象内容。
Pascal语言是为了提倡结构化编程而发明的语言。
BASIC语言为了让大学生容易地控制计算机开发的语言,其特点是简单易懂,且可以用解释和编译两种方式执行。C++语言是一种静态数据类型检查的、支持多重编程范式的通用程序设计语言,它支持过程化程序设计、数据抽象、面向对象程序设计、泛型程序设计等多种程序设计风格。
Java语言是一种面向对象语言,从语法结构上看,与C++类似。

2.下列说法中,错误的有()。

A.Java面向对象语言容许单独的过程与函数存在。
B.Java面向对象语言容许单独的方法存在。
C.Java语言中的方法属于类中的成员(member)。
D.Java语言中的方法必定隶属某一类(对象),调用方法与过程或函数相同。

答案:A、B、C。
A.Java是面向对象的语言而不是面向过程的语言。
B:Java中方法不能单独存在,所有的方法必须定义在类中。
C:Java语言中的方式属于对象的成员,而不是类的成员。

3.Java程序中程序运行入口方法main的签名正确的有()。

A.public static void main(String[] args)
B.public static final void main (String[] args)
C.static public void main (String[] args)
D.static public synchronized void main(String[] args)
E.static public abstract void main(String[] args)

答案:A、B、C、D
E:由于main()方法为程序的入口方法,因此不能用abstract关键字修饰,因为入口方法必须使用static修饰,不允许被子类覆盖,而abstract修饰的方法允许被子类覆盖,所以二者存在冲突。
由于public与static没有先后顺序关系,所以static可以定义在public前面。
final与synchronized也可以修饰main()方法。

4.众所周知,在Java语言中,main()方法是程序的入口方法,在程序运行时,最先加载的就是main()方法,但这是否意味着main()方法就是程序运行时第一个被执行的模块呢?

答案:否定!
在Java语言中,由于静态块在类被加载时就会被调用,因此可以在main()方法执行前,利用静态块实现输出,例如以下程序

public class Test{
	static{
		System.out.println("Hello World!");
	}
	public static void main(){
		System.out.println("Hello World2!");
	}
}

程序运行结果:
Hello World!
Hello World2!
原因:由于静态代码块不管顺序如何,都会在main()方法执行之前执行,因此,以下代码会与上面的代码有同样的输出结果。

public class Test{
	public static void main(){
		System.out.println("Hello World2!");
	}
	static{
		System.out.println("Hello World!");
	}
}

5.以下程序的运行结果是什么?

class B extends Object{
	static{
		System.out.println("Load B1");
	}
	public B(){
		System.out.println("Create B");
	}
	static{
		System.out.println("Load B2");
	}
}
class A extends B{
	static{
		System.out.println("Load A");
	}
	public A(){
		System.out.println("Create A");
	}
}
public class Testclass{
	public static void main(String[] args){
		new A();
	}
}
A.Load B1		Load B2	 	Create B	Load A 			Create A
B.Load B1		Load B2		Load A		Create B		Create A
C.Load B2		Load B1		Create B	Create A		LoadA
D.Create B		Create A	Load B1		Load B2			Load A

答案:B
原因:子类继承父类,先执行父类的静态方法,再执行子类的静态方法,其次在执行父类的构造方法,最后执行子类的构造方法;这四步优先执行于非静态,非构造方法之前。-----总结:有静态执行静态,无静态执行构造,无构造在执行普通方法调用。

6.下列说法中,正确的是()

A.实例方法可直接调用超类 的实例方法.
B.实例方法可直接调用超类的类方法.
C.实例方法可以直接调用其他类的实例方法.
D.实例方法可直接调用本类的类方法.

答案:D
原因说明:当超类的实例方法已经类方法为private时,是不能被子类调用的;当其他类的实例方法为private时.也不能被其他类直接调用。

7.一个Java文件是否可以定义多个类?

答案:一个Java文件中可以定义多个类,但是只有一个类被public修饰,并且这个类名与文件名必须相同,若这个文件没有public的类,则文件名随便是一个类的名字即可。需要注意的是,当用javac指令编译这个.java文件时,它会给每一个类生成一个对应的.class文件,如下例Derived.java为

class Base{
	public void print(){
		System.out.println("Base");
	}
}
public class Derived extends Base{
	public static void main(String[] a){
		Base c=new Derived();
		c.print();
	}
}

使用javac Derived.java指令编译上述代码,会生成两个字节码文件:Base.class与Derived.class,然后使用java Derived指令执行代码,此时,控制台的输出结果为:Base。

8.下列关于构造方法叙述中,错误的是()

A.Java语言规定构造方法名与类名必须相同。
B.Java语言规定构造方法没有返回值,但不用void声明。
C.Java语言规定构造方法不可以重载。
D.Java语言规定构造方法只能通过new自动调用。

答案:C。可以定义多个构造函数,只要不同的构造函数有不同的参数即可。

9.下列说法中,正确的是()

A.class中的constructor不可省略。
B.constructor必须与class同名,但方法不能与class同名。
C.constructor在一个对象被new时执行。
D.一个class只能定义一个constructor。

答案:c。
A.class的constructor可以省略不写。底层执行的时候回自动补全。
B.constructor必须与class同名,方法可以与class同名。
D.一个class可以定义多个constructor,只要参数值不同就可以了。

10.不能用来修饰interface的有()

A.private			B.public			C.protected			D.static

答案:A、C、D。
接口只包含方法的定义,没有方法的实现。接口中的所有方法都是抽象的。接口中成员的作用域修饰符都是public,接口中常量值默认使用public static final修饰。由于一个类可以实现多个接口,因此通常可以实现多个接口的方式来间接达到多重继承目的。

11.Java创建对象的方式有几种?

答案:共有四种创建对象的方法。
1)通过new语句实例化一个对象。
2)通过反射机制创建对象。
3)通过clone()方法创建一个对象。
4)通过反序列化的方式创建对象

12.下列说法中,正确的是()

A.Java中包的主要内容是实现跨平台功能。
B.package语句只能放在import语句后面。
C.包(package)	由一组类(class)和接口(interface)组成。
D.可以用#include关键字来表明来自其他包中的类。

答案:C。
A:java中包的主要作用是把.java文件(Java'源文件)、.class文件(编译后的文件)以及其他resource文件(例如.xml文件、.avi文件、.MP3文件、txt文件等)有条理地进行一个组织,以供使用。
B:packge语句放在import语句之前。
D:#include是放在C++语言中的以及是javaweb'。

13.面向对象有哪些特征

面向对象的主要特征包含抽象、继承、封装和多态。
1):抽象:抽象就是忽略一个主题与当前目标无关的那些方面,以便充分地注意与当前目前有关的方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节。抽象包括两个方面:一种是过程抽象;而是数据抽象。
2):继承。继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法。对象的一个新类可以从现有类中派生,这个过程称为类继承。新类继承了原始类的特性,新类称为原始类的派生类(子类),而原始类称为新类的基类(父类)。派生类可以从它的基类那里继承方法和实例变量,并且派生类可以修改或增加新的方法使之更适合特殊的需要。
3)封装。封装是指将客观事物抽象成类,每个类对自身的数据和方法实行保护。类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
4)多态。多态是指允许不同类的对象对同一消息作出响应。多态包括参数化多态和包含多态。多态性语言具有灵活、抽象、行为共享等优势,很好地解决了应用程序函数同名问题。

14.面向对象的开发方式有什么优点

采用面向对象的开发方式有诸多的优点,下面主要介绍其中3个优点。

  1. 较高的开发效率。采用面向对象的开发方式,可以对现实的事物进行抽象,可以把现实的事物直接映射为开发的对象,与人类的思维过程相似,例如可以设计一个Car类来表示现实中的汽车,这种方式非常直观明了.也非常接近人们的正常思维。同时,由于面向对象的开发方式可以通过继承或者组合的方式来实现代码的重用,因此可以大大地提高软件的开发效率。
  2. 保证软件的鲁棒性。正是由于面向对象的开发方式有很高的重要性,在开发的过程中可以重用已有的而且在相关领域经过长期测试的代码,因此,自然而然地对软件的鲁棒性起到良好的促进作用。
  3. 保证软件的高可维护性。由于采用面向对象的开发方式,使得代码的可读性非常好,同时面向对象的设计模式也使得diamante结构更加清晰明了。同时针对面向对象的开发方式,已有许多非常成熟的设计模式,这些设计模式可以使程序在面对需求的变更时,只需要修改部分的模块就可以满足需求,因此维护起来非常方便。

15.什么是继承?

答案:继承是面向对象中的一个非常重要的特性。通过继承.子类可以使用父类中的一些成员变量与方法,从而能提高代码的复用性,提高开发效率。在Java语言中,被继承的类叫做基类(superclass)或父类,继承基类或父类的;;类叫派生类或子类(subclass)。继承是通过extends关键字来实现的,使用格式为:class子类名extends父类名。

16.继承有什么特性?

答案:java语言不支持多重继承,也就是说,子类至多只能有一个父类,但是可以通过实现多个接口来达到多重继承的目的。
子类只能继承父类的非私有(public与protected)成员变量与方法。
当子类中定义的成员变量和父类中定义的成员变量同名时,子类的成员变量会覆盖父类的成员变量,而不会继承。
当子类中的方法与父类中的方法有相同的函数签名(相同的方法名,相同的参数个数与类型)时,子类将会覆盖父类的方法,而不会继承。

17.下列有关继承的说法中,正确的是()

A.子类能继承父类的所有方法和状态
B.子类能继承父类的非私有方法和状态
C.子类只能继承父类public方法和状态
D.子类能继承父类的方法,而不是状态

答案:B。

18.多态的实现机制是什么?

答案:多态是面向程序设计代码重要的一个重要机制,它表示当同一个操作作用在不同对象时,会有不同的语义,从而会产生不同的结果,例如,同样是执行"+"操作,"3+4"用来实现整数相加,而"3"+"4"却实现了字符串的连接。在Java语言中,多态主要有以下两种方式:
1)方法的重载(overload)。重载是指同一类中有多个同名的方法,但这些方法有着不同的参数,因此在编译时就可以确定到底调用哪个方法,它是一种编译时多态。重载可以被看作一个类中的方法多态性。
2)方法的覆盖(override)。子类可以覆盖父类的方法,因此同样的方法会在父类 与子类中有着不同的表现形式。在Java语言中,基类的引用变量不仅可以指向基类的实例对象,也可以指向其子类的实例对象。同样,接口的引用变量也可以指向其实现类的实例对象。而程序调用的方法在运行期才动态绑定(绑定指的是将一个方法调用和另一个方法主体链接到一起),就是引用变量所指向的具体实例对象的方法,也就是内存里正在运行的那个对象方法,而不是引用变量的类型中定义的方法。通过这种动态绑定的方法实现了多态。由于只有在运行时才能确定调用哪个方法,因此通过方法覆盖实现的多态也可以被称为运行时多态。

19.Java中提供了哪两种用于多态的机制?

答案:编译时多态和运行时多态。编译时多态是通过方法的重载实现的,运行时多态是通过方法的覆盖(子类覆盖父类方法)实现的。

20.重载和覆盖有什么区别

答案:
1)覆盖是子类和父类之间的关系,是垂直关系;重载是同一个类中方法之间的关系,是水平关系。
2)覆盖只能由一个方法或只能由一对方法产生关系;重载是多个方法之间的关系。
3)覆盖要求参数列表相同;重载要求参数列表不同。
4)覆盖关系中,调用方法体是根据对象的类型(对象对应存储空间类型)来决定;而重载关系是根据调用时的实参表与形参表来选择方法体的。

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

答案:如果一个类中包含抽象方法,那么这个类就是抽象类。在Java语言中,可以通过把类或者类中的某些方法声明为abstract(abstract只能用来修饰类或者方法,不能用来修饰属性)来表示一个类是抽象类。接口就是指一个方法的集合,接口中的所有方法都没有方法体,在Java语言中,接口是通过关键字interface来实现的。
抽象类(abstract class)和接口(interface)都是支持抽象定义的两种机制(注意:此句中的前后两个抽象类的意义不一样,前者表示的是一个实体,后者表示的是一个概念)。二者具有很大的相似性,甚至有时候是可以互相的。但同时,二者也存在很大的区别。
只要包含一盒抽象方法的类就必须被声明为抽象类,抽象类可以声明方法的存在而不去实现它,被声明为抽象的方法不能包含方法体。在实现时,必须包含相同的或者更低的访问级别(public-->protected-->private)。抽象类在使用的过程汇总不能被实例化,但是可以创建一个对象使其指向具体子类的一个实例。抽象类的子类为父类中的所有抽象方法提供具体的实现,否则它们也是抽象类。接口可以看作抽象类的变体。接口中所有方法都是抽象的,可以通过接口来间接地实现多重继承。接口中的成员变量都是static final类型。由于抽象类可以包含部分方法的实现,因此,在一些场合下抽象类比接口存在更多的优势。

答案:接口与抽象类的相同点如下:
1)都不能被实例化。
2)接口的实现类或抽象类的子类都只有实现了接口或抽象类中的方法后才能被实例化。

答案:接口与抽象类的不同点如下:
1)接口只有定义,其方法不能再接口中实现,只有实现接口的类才能实现接口中定义的方法,而抽象类可以有定义与实现,即其方法可以抽象类中被实现。
2)接口需要实现(用implements),但抽象类只能继承(用extends)。一个类可以实现多个接口,但一个类只能继承一个抽象类,因此使用接口可以间接地达到多重继承的目的。
3)接口强调特定的实现,其设计理念"has - a" 关系;而抽象类强调所属关系,其设计理念为 "is - a" 关系。
4)接口中定义的成员变量默认为public static final,只能够有静态的不能被修改的数据成员,而且,必须给其赋初值,其所有成员方法都是public、abstract的,而且只能被这两个关键字修饰。而抽象类可以有自己的数据成员变量,也可以有非抽象的成员方法,而且,抽象类中的成员变量默认为default(本包可见),当然也可以被定义为private、protected和public,这些成员变量可以在子类中被重新定义,也可以被重新赋值,抽象类中的抽象方法(其前有abstract修饰)不能用private、static、synchronized、native等访问修饰符修饰,同时方法必须以分号结尾,并且不带花括号。所以,当功能需要累积时,用抽象类;不需要累积时,用接口。
5)接口被运用于实现比较常用的功能,便于日后维护或者添加删除方法;而抽象类更倾向于充当公共类的角色,不适用于日后重新对里面的代码进行修改。
简单点说,接口是一种特殊形式的抽象类,使用接口完全有可能实现与抽象类相同的操作,但一般而言,抽象类多用于在同类事物中有无法具体描述的方法的场景,所以当子类和父类之间存在有逻辑上的层次结构时,推荐使用抽象类;而接口多用于不同类之间,定义不同类之间的通信规则,所以当希望支持差别较大的两个或者更多对象之间的特定交互行为时,应该使用接口。
此外,接口可以继承接口,抽象类可以实现接口,抽象类也可以继承具体类。抽象类也可以有静态的main方法。

22.下列关于接口的定义中,正确的是()

A.void methoda();
B.public double methoda();
C.public final double methoda();
D.static void methoda(double dl);
E.protected void methoda(double dl);
F.int a;
G.int b=1;

答案:A、B、G。
从上面的分析可知,接口中的方法只能用关键字public和abstract来修饰,因此选项C、D、E都是错误的。接口中的属性默认都为public static final,由于属性被fianl修饰,因此它是常量,常量在定义时就必须初始化,因此F是错误的。

23.下列说法中,正确的是().

A.声明抽象方法大括号可有可无。
B.声明抽象方法不可写出大括号。
C.抽象方法有方法体。
D.abstract可修饰属性、方法和类

答案:B。
抽象方法不能有方法体,同理也就不能有大括号。abstract只能用来修饰类与方法,不能用来修饰属性。

24.定义如下一个外部类。先需要在这个外部类中定义一个内部类,下面哪个定义是正确的?

	public class OuterClass{
		private int dl=1;
		//编写内部类
	}
A. 	class InnerClass{
		public static int mehoda(){
			return dl;
		}
	}
B.	public class InnerClass{
		static int methoda(){
			return dl;
		}
	}
C.	private class InnerClass{
		int methoda(){
			return dl;
		}
	}
D.	static class InnerClass{
		protected int methoda(){
			return dl;
		}
	}
E.	abstract class InnerClass{
		public abstract int methoda(){
			
		}
	}

答案:C、E。由于在非静态内部类中不能定义静态成员,因此A和B是错误的。由于静态内部类不能访问外部类的非静态成员,因此D是错误的。

25.下列程序的运行结果是什么?

	class Base{
		public Base(){
			System.out.println("Base");
		}
	}
	class Sub extends Base{
		public Sub(){
			System.out.println("Sub");
			super();//语法错误,当子类需要显示调用父类构造函数时,super()必须为构造函数的第一条语句
		}
	}
	public class Test{
		public static void(String[] args){
			Base s=new Sub();
		}
	}

答案:编辑错误。
当子类构造函数需要显示调用父类构造函数时,super()必须为构造函数中的第一条语句,因此正确的写法如下:

public Sub(){
	super();
	System.out.println("Sub");
}

26.下列不属于Java标识符的是()

A._HelloWorld
B.3HelloWorld
C.$HelloWorld
D.HelloWorld3

答案:B。
Java的标识符第一个字符必须是字母、下划线或$

27.下列标识符不合法的有()

A.new			B.$usdollars			C.1234			D.car.taxi

答案:A、C、D
A,new 是java语言已经写好的关键词不能作为标识符
C.Java的标识符第一个字符必须是字母、下划线或$
D.Java的标识符只能有字母、数字、下换线(_)或$组成

28.final、finally和finalize有什么区别

答案:final、finally和finalize的区别如下:
1)final用于声明属性、方法和类,分别表示属性不可变、方法不可覆盖和类不可被继承(不能再派生出新的子类)。
final属性:被final修饰的变量不可变。由于不可变有两重含义:一是引用不可变;二是对象不可变。final指的是引用的不可变性,即它只能指向初始化时指向的那个对象,而不关心指向对象内容的变化。所以,被final修饰的变量必须被初始化。一般可以通过以下几种方式对其进行初始化:①在定义的时候初始化。②final成员变量可以在初始化块中初始化,但不可在静态初始化块中初始化。③静态final成员变量可以在静态初始化块中初始化,但不可在初始化块中初始化。④在类的构造器中初始化,但静态final成员变量 不可以在构造函数初始化。④在类的构造器中初始化,但静态final成员变量不可以在构造函数中初始化。
final方法:当一个方法声明为final时,该方法不允许任何子类重写这个方法,但子类仍然可以使用这个方法。另外,还有一种被称为inline(内联)的机制,当调用一个被声明为final的方法时,直接将方法主体插入到调用处,而不是进行方法调用(类似于C++中的inline),这样做能提高程序的效率。
final参数:用来表示这个参数在这个函数内部不允许被修改。
final类:当一个类被声明为final时,此类不能被继承,所有方法都不能被重写。但这并不表示final类的成员变量也是不可改变的,要想做到final类的成员变量不可改变,必须给成员变量增加final修饰。值得注意的是,一个类不能既被声明为abstract,又被声明为final。
2)finally作为异常处理的一部分,它只能用在try/catch语句中,并且附带一个语句块,表示这段语句最终一定被执行,经常被用在需要释放资源的情况下。
3)finalize是Object类的一个方法,在垃圾回收器执行时会调出被回收对象的finalize()方法,可以覆盖此方法来实现对其资源的回收,例如关闭文件等。需要注意的是,一旦垃圾回收器准备好释放对象占用的空间,将首先调用其finalize()方法,并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。

29.JDK中哪些类不能继承的?

答案:不能继承用final关键字修饰的类。一般比较基本的类型为防止扩展类无意间破坏原来方法的实现的类型都应该是final的,在JDK中,String、StringBuffer等都是基本类型,所以,String、StringBuffer等类是不能继承的。

30.assert有什么用

答案:断言(assert)作为一种软件调试的方法,提供了一种在代码中进行正确性检查的机制,目前很多开发语言都支持这种机制。它的主要作用是对一个boolean表达式进行检查,一个正确运行的程序必须保证这个boolean表达式的值为true,若boolean表达式的值为false,则说明程序已经处于一种不正确的状态下,系统需要提供告警信息并且退出程序。在实际的开发中assert主要用来保证程序的正确性,通常在程序开发和测试时使用。为了提高程序运行的效率,在软件发布后,assert检查默认是被关闭的。
assert包括两种表达式,分别是assert expressionl与assert expression1:expression2,其中,expression1表示一个boolean表达式,expression2表示一个基本类型或者是一个对象,基本类型包括boolean、char、double、float、int和long。
assert的应用范围很多,只要包括①检查控制流;②检查输入参数是否有效;③检查函数结果是否有效;④检查程序不变量。虽然assert的功能与if判断类似,但二者存在着本质的区别:assert一般咋调试程序时使用,但如果不小心用assert来控制了程序的业务流程,那在调试结束后去掉assert就意味着修改了程序的正常逻辑,这样的做法是非常危险的;而if判断是逻辑判断,本身就是用以控制程序流程的。
需要注意的是,在Java语言中,assert与C语言中的assert尽管功能类似,但也不完全一样,具体表现为两个方面的不同:①Java语言中是使用assert关键字去实现其功能,而C语言中使用的是库函数;②C语言中的assert是在编译时开启,而Java语言中则是在运行时开启。

31.static关键字有哪些作用

答案:static关键字主要有两种作用:第一,为某特定数据类型或对象分配单一的存储空间,而与创建对象的个数无关。第二,实现某个方法或属性与类而不是对象关联在一起,也就是说在不创建对象的情况下就可以通过类来直接调用方法或使用类的属性。具体而言,在Java语言中,static主要有4种使用情况:成员变量、成员方法、代码块和内部类。
1)static成员变量
虽然Java语言中没有全局的概念,但可以通过static关键字来达到全局的效果。Java类提供了两种类型的变量:用static关键字修饰的静态变量和不用static关键字修饰的实例变量。静态变量属于类,在内存中只有一个复制(所有实例都指向同一个内存地址),只要静态变量所在类被加载,这个静态变量就会被分配空间,因此就可以被使用了。对静态变量的引用有两种方式,分别为"类·静态变量"和"对象·静态变量"。
实例变量属于对象,只有对象被创建后,实例变量才会被分配空间,才能被使用,它在内存中存在多个复制。只能用"对象·实例变量"的方式来引用。
2)static成员方法
与变量类似,Java类同时也提供了static方法与非static方法。static方法是类的方法,不需要创建对象就可以被调用,而非static方法是对象的方法,只有对象被创建出来后才可以被使用。
static方法中不能使用this和super关键字,不能调用非static方法,只能访问所属类的静态成员变量和成员方法,因为当static方法被调用时,这个类的对象可能还没被创建,即使已经被创建了,也无法确定调用哪个对象的方法。同理,static方法也不能访问非static类型的变量。
static一个很重要的用途是实现单例模式。单例模式的特点是该类只能有一个实例,为了实现这一功能,必须隐藏类的构造函数,即把构造函数声明为private,并提供一个创建对象的方法,由于构造对象被声明为private,外界无法直接创建这个类型的对象,只能通过该类提供的方法来获取类的对象,要达到这样的目的只能把创建对象的方法声明为static。
用public修饰的static变量和方法本质上都是全局的,若在static变量前用private修饰则表示这个变量可以在类的静态代码块或者其他静态成员方法中使用,但是不能在其他类中通过类名来直接引用。
3)static代码块
static代码块(静态代码块)在类中是独立于成员变量和成员函数的代码块的。它不在任何一个方法体内,JVM在加载类时会执行static代码块,如果有多个static代码块,JVM将会按顺序来执行。static代码块经常被用来初始化静态变量。需要主要的是,这些static代码块只会被执行一次。
4)static内部类
static内部类是指被声明为static的内部类,它可以不依赖与外部类实例对象而被实例化而通过的内部类需要在外部类实例化后才能实例化。静态内部类不能与外部类有相同的名字,不能访问外部类的普通成员变量,只能访问外部类中的静态成员和静态方法(包含私有类型)

32.Java提供了哪些基本数据类型

Java语言一共提供了8种原始的数据类型(byte,short,int,long,float,double,char,boolean),这些数据类型不是对象,而是Java语言中不同于类的特殊类型,这些基本类型的数据变量在声明之后就会立刻在栈上被分配内存空间。除了这8种基本的数据类型外,其他类型都是引用类型(例如类、接口、数组等),引用类型类似于C++中的引用或指针的概念,它以特殊的方式指向对象实体,这类变量在声明时不会被分配内存空间,只是存储了一个内存地址而已。

 数据类型	字节长度		范围															默认值		包装类
 int		4			[-2147483648,2147483647](-2^31~2^31-1)						0			Integer
 short		2			[-32768,23767]												0			Short
 long		8			[-9223372036854775808,9223372036854775807](-2^63~2^63-1)	OL或ol		Long	
 byte		1			[-128,127]													0			Byte
 float		4			32位IEEE754单精度范围											0.0F0.0f	Float
 double		8			64位IEEE754双精度范围											0.0			Double
 char		2			Unicode[0,65535]											u0000		Character
 boolean	1			truefalse													false		Boolean

以上这些基本类型可以分为如下4种类型:
1)int长度数据类型:byte(8bit)、short(16bit)、int(32bit)、long(64bit)。
2)float长度数据类型:单精度(32bit float)、双精度(64bit double)。
3)boolean类型变量的取值:true、false。对于boolean占用空间的大小,从理论上讲,只需要1bit就够了,但在设计的时候为了考虑字节对齐等因素,一般会考虑使其占用一个字符。由于Java规范没有明确的规定,因此,不同的JVM可能会有不同的实现。
4)char数据类型:unicode字符(16bit)。
此外,Java语言还提供了对这些原始数据类型的封装类(字符类型Charater,布尔类型Boolean,数值类型Byte、Short、Interget、Long、Float、Double)。需要注意的是,Java中的数值类型都是有符号的,不存在无符号的数,它们的取值范围也是固定的,不会随着硬件环境或者操作系统的改变而改变。除了以上提到的8种基本数据类型以外,在Java语言中,还存在另外一种基本类型void,它也有对应的封装类java.lang.void,只是无法直接对它进行操作而已。封装类型和原始类型有许多不同点:首先,原始数据类型在传递参数时都是安值传递,而封装类型是按引用传递的。其次,当封装类型和原始类型用作某个类的实例数据时,它们所指定的默认值不同。对象引用实例变量的默认值为null,而原始类型实例变量的默认值与它们的类型有关。

33.在Java语言中null值是什么?在内存中 null是什么?

null不是一个合法的Object实例,所以编译器并没有为其分配内存,它仅仅用于表明该引用目前没有指向任何对象。其实,与C语言类似,null是将引用变量的值全部置0。

34.如何理解赋值语句String x=null?

在Java语言中,变量被分为两大类型:原始值(primitive)与引用值(reference)。声明为原始类型的变量,其存储的是实际的值。声明为引用类型的变量,存储的是实际对象的地址(指针,引用)。对于赋值语句String x=null.它定义了一个变量 "x",x中存放的是String引用,此处为null。

35.下列表达式中,正确的是().

A.byte b=128;		B.boolean flag=null;		C.float f=0.9239		D.long a=2147483648L;

答案: D。
A中byte能表示的取值范围为[-128,127],因此不能表示128。
B中boolean的取值只能是true或false,不能为null。
C中0.9239为double类型,需要进行数据类型转换。

36.String是最基本的数据类型吗?

答案:不是。基本数据类型包括byte、int、char、long、float、double、boolean和short。

37.int和Integer有什么区别?

答案:Java语言提供两种不同的类型,即引用类型和原始类型(或内置类型)。int是Java语言的原始数据类型,Integer是Java语言为int提供的封装类。
引用类型与原始类型的行为完全不同,并且它们具有不同的语义。而且,引用类型与原始类型具有不同的特征和用法。

38.赋值语句float f=3.4是否正确

答案:不正确。数据3.4默认情况下是double类型,即双精度浮点数,将double类型数值赋值给float类型的变量,或造成精度损失,因此需要对其进行强制类型转换,即将3.4转换成float类型或者将3.4强制携程float类型。所以,float f=(float) 3.4或者float f=3.4F写法都是可以的。

39.什么是不可变类

答案:不可变类(immutable class)是指当创建了这个类的实例后,就不允许修改它的值了,也就是说,一个对象一旦被创建出来,在其整个生命周期钟,它的成员变量就不能被修改了。它有点类似于常量(const),即只允许别的程序读,不允许别的程序进行修改。

40.创建一个不可变类需要遵循那些原则

答案:创建一个不可变类需要遵循以下四个原则。
1、类中所有成员变量被private所修饰。
2、类中没有写或者修改成员变量的方法,例如setxx,只提供构造函数,一次生成,永不改变。
3、确保类中所有方法不会被子类覆盖,可以通过把类定义为final或者把类中的方法定为final来达到这个目的。
4、如果一个类成员不是不可变量,那么在成员初始化或者使用get方法获取该成员变量时,需要通过clone方法来确保类的不可变性。
5、如果有必要,可使用覆盖Object类的equals()方法和hashCode()方法。在equals()方法中,根据对象的属性值来比较两个对象是否相等,并且保证用equals()方法判断为相等的两个对象的hashCode()方法的返回值也相等,这可以保证哲学对象能被正确地放到HashMap或HashSet集合中。

41.值传递与引用传递有哪些区别

答案:方法调用是编程语言中非常重要的一个特性,在方法调用时,通常需要传递一些参数来完成特定的功能。Java语言提供了两种参数传递的方式:值传递和引用传递。
1)值传递
在方法调用中,实参会把它的值传递给形参,形参只是实参的值初始化一个临时的存储单元,因此形参与实参虽然有着相同的值,但是却有着不同的存储单元,因此对形参的改变不会影响实参的值。
2)引用传递
在方法调用中,传递的是对象(也可以看作是对象的地址),这时形参与实参的对象指向同一块存储单元,因此对形参的修改就会影响实参的值。
注解:在Java语言中,原始数据类型在传递参数时都是按值传递,而包装类型在传递参数时是按引用传递的。

42.下列说法正确的是

A.call by value不会改变实际参数的值
B.call by reference能改变实际参数
C.call by reference 不能改变实际参数的地址
D.call by reference 能改变实际参数的内容

答案:A、C、D。

43.不同数据类型的转换有哪些规则

答案:在Java语言,当参与运算的两个变量的数据类型不同时,就需要进行隐式的数据类型转换,转换的规则为:从低精度向高精度转换,即优先级满中byte<short<char<int<long<float<double,例如,不同数据类型的值在进行运算时,short类型数据能够自动转为int类型,int类型数据能够自动转换为float类型等。反之,则需要通过强制类型转换来实现。
(1)类型自动转换
低级数据类型可以自动转换为高级数据类型。

操作数1的类型		操作数2的类型							转换后的类型
long			byte short char int					long
int				byte short char						int
float			byte short int char long			float
double			byte short int long char float		double

当类型自动转换时,需要注意以下几点:
1)char类型的数据转换为高级类型(如int,long等),会转换为其对应的ASCII码。
2)byte、char、short类型的数据在参与运算时会自动转换为int型,但当使用"+="运算时,就不会产生类型的转换。
3)另外,在java语言中,基本数据类型与boolean类型是不能相互转换的。
总之,当有很多中类型的数据混合运算时,系统会先自动地将所有数据转换成容量最大的那一种数据类型,然后在进行计算。
(2)强制类型转换
当需要从高级数据转换为低级数据类型时,就需要进行强制类型转换。

原操作数的类型			转换后操作数的类型
byte					char
char					byte char
short					byte char
int						byte short char
long					byte short char int
float					byte short char int long
double					byte short char int long double

注意:在进行强制类型转换时可能会损失精度。

44.下面程序的运行结果是什么?

int i=1;
if(i)
	System.out.println("true");
else
	System.out.println("false");

答案:编译错误。因为if条件只接受boolean类型的值(true或false),而i的类型为int,int类型不能被隐式地转换为boolean类型。

45.对于下述代码结果强制类型转换后,变量a和b的值分别为()

short a=128;
byte b=(byte)a;

答案:a=128,b=-128。short类型变量占两个字节,a对应的二进制为:00000000 10000000,由于byte只占一个字节,在强制转换为byte的时候只截取低字节:10000000 10000000是-128的补码,因此b的值为-128。

46.强制类型转换的注意事项有哪些

答案:Java语言在涉及byte、short和char类型的运算时,首先会把这些类型的变量值强制转换为int类型,然后对int类型的值进行计算,最后得到的值也是int类型。因此,如果把两个short类型的值相加,最后需要得到short类型的结果,就必须显式地把运算结果转换为short类型,例如对于语句short s1=1;s1=s1+1,由于在运行时会首先将s1转换成int类型,因此s1+1的结果为int类型,这些编译器会报错,所以,正确的写法应该short s1=1;s1=(short)(s1+1)。

47.运算符优先级是什么

优先级			运算符						结合性
1				.()[]
2				+() -() ++ -- ~ !
3				* / %
4				+() -()
5				<< >>(无符号右移)  >>>(有符号右移)
6				< <= > >= instanceof
7				== !=
8				&
9				|
10				^
11				&&
12				| |
13				?:
14				= += -= *= /= %= &= =|= ^= ~= <<= >>= >>>=

48.下面程序的运行结果是什么?

public class Test{
	public static void main(){
		byte a=5;
		int b=10;
		int c=a>>2+b>>2;
		System.out.println(c);
	}
}

答案:0。由于"+"的优先级比">>"高,因此程序中的表达式等价于a>>(2+b)>>2,相当于a>>12>>2,因此运行结果为0。

49.Math类中round、ceil和floor方法的功能各是什么

答案:round、ceil和floor方法位于Math类中,Math是一个包含了很多数据常量与计算方法的类,位于java.lang包下,能自动导入,而且Math类里边的方法全是静态方法。下面重点介绍了三个方法:
1)round方法表示四舍五入。round,意为"环绕",其实实现原理是在原来数字的基础上先增加0.5然后再向下取整,等同于(int)Math.floor(x+0.5f)。它的返回值类型为int型,例如,Math.round(11.5)的结果为12,Math.round(-11.5)的结果为-11。
2)ceill方法的 功能是向上取整。ceil,意为"天花板",顾名思义是对操作数取顶,Math.ceil(a),就是取大于a的最小的整数值。需要注意的是,它的返回值类型并不是int型,而是double型。若a是正数,则把小数"入",若a是负数,则把小数"舍"。
3)floor方法的功能是向下取整。floor,意为"地板",顾名思义是对操作数取低。Math.floor(a),就是取小于a的最大的整数值。它的返回值类型与ceil方法一样,也是double型。若a是正数,则把小数"舍";若a是负数,则把小数"入"。

50.Math.round(11.5)等于多少?Math.round(-11.5)等于多少?

答案:12,-11。

51.假设x=1,y=2,z=3,则表达式y+=z–/++x的值是

A.3		B.3,5		C.4		D.5

答案:A。

52.在Java语言中,下列关于字符集编码(Character Set Encoding)和国际化(il8n)的叙述,哪些是正确的?()

A.每个中文字符占用2Byte,每个英文字符占用1Byte。
B.假设数据库中的字符是以GBK编码的,那么显示数据库数据的网页也必须是GBK编码的。
C.Java的char类型,以UTF-16 Big Endian的方式保存一个字符。
D.实现国际化应用常用的手段是利用ResourceBundle类。

答案:A、D。
数据库与Web页面可以有各自的编码,二者没有必然的关系。
ResourceBundle是一个资源处理类,可以经常在国际化应用中使用。

53.字符串创建与存储的机制是什么

在Java语言中,字符串起着非常重要的作用,字符串的声明与初始化主要有如下两种情况:
1)对于String s1=new String("abc")语句与String s2=new String("abc")语句,存在两个引用对象s1、s2,两个内容相同的字符串对象"abc",它们在内存中的地址是不同的。只要用到new总会生成新得对象。
2)对于String s1="abc"语句与String s2="abc"语句,在JVM中存在着一个字符串池,其中保存着很多String对象,并且可以被共享使用,s1、s2引用得是同一个常量池中得对象。由于String得实现采用了Flyweight得设计模式,当创建一个字符串常量时.例如String s="abc",会首先在字符串常量池中查找是否已经有相同得字符串被定义,其判断依据是String类equals(Object obj)方法得返回值。若已经定义,则直接获取对其的引用,此时不需要创建新的对象;若没有定义,则首先创建这个对象,然后把它加入到字符串池中,再将它的引用返回。由于String是不可变类,一旦创建好了就不能被修改,因此String对象可以被共享而且不会导致程序混乱。

54.new String(“abc”)创建了几个对象?

一个或两个。如果常量池中原来有"abc",那么只创建一个对象;如果常量池中原来没有字符串"abc",那么就会创建两个对象。

55."=="、equals和hashCode有什么区别

1)"=="运算符用来比较两个变量的值是否相等。也就是说,该运算符用于比较变量对应的内存中所存储的数值是否相同,要比较两个基本类型的数据或两个引用变量是否相等,只能使用"=="运算符。来比较其对应的值是否相等。如果一个变量指向的数据是对象(引用类型),那么,此时涉及了两块内存,对象本身占用一块内存(堆内存),变量也占用一块内存,例如,对于赋值语句String s=new String(),变量s占用一块存储空间,而new String()则存储再另外一块存储空间里,此时,变量s所对应内存中存储的数值就是对象占用的那块内存的首地址。对于指向对象类型的变量,如果要比较两个变量是否指向同一个对象,既要看这两个变量所对应内存中的数值是否相等(这两个对像是否指向同一块存储空间),这时候就可以用"=="运算符进行比较。但是,如果要比较这两个对象的内容是否相等,那么用"=="运算符就无法实现了。
2)equals是Object类提供的方法之一。每一个Java类都继承自Object类,所以每一个对象都具有equals这个方法。Object类中定义的equals(Object)方法是直接使用"=="运算符比较的两个对象,所以再没有覆盖equals(Object)方法是直接使用"=="运算符比较的两个对象,所以在没有覆盖equals(Object)方法的情况下,equals(Object)与"=="运算符一样,比较的是引用。
相比"=="运算符,equals(Object)方法的特殊之处就在于它可以被覆盖,所以可以通过覆盖的方法让它不是比较而是比较数据内容,例如String类的equals方法是用于比较两个独立对象的内容是否相同,即堆中的内容是否相同。

56.假设有以下代码String s=“hello”; String t =“hello”;char c[]={‘h’,‘e’,‘l’,‘l’,‘o’},下列选项中返回false的语句的是:()

A.s.equals(t)		B.t.equals(c)		C.s==t			D.t.equals(new String("hello"))

答案:B。

57.下面程序的输出结果是什么?

String s="abc";
String s1="ab"+"c";
System.out.println(s==s1);

答案:true。
在编译器中"ab"+"b"自动转换成"abc,存放在常量区,所以输出结果为true"

58. Set里的元素不能重复,那么用什么办法来区分是否重复呢?是用"=="还是equals()?它们有什么差别?

答案:用equals()方法来区分是否重复。
"=="运算符用来比较两个变量的值是否相等。也就是说,该运算符用于比较变量对应的内存中所存储的数值是否相同,要比较两个基本类型的数据或两个引用变量是否相等,只能使用"=="运算符。
equals是Object类提供的方法之一。每一个Java类都继承自Object类,所以每一个对象都具有equals这个方法。Object类中定义的equals(Object)方法是直接使用"=="运算符比较的两个对象,所以在没有覆盖equals(Object)方法的情况下,equals(Object)与"=="运算符一样,比较的是引用。

59.String、StringBuffer、StringBuilder和StringTokenizer有什么区别

Java语言有四个类可以对字符或字符串进行操作,它们分别是Character、String、StringBuffer和StringTokenizer,其中Character用于单个字符串操作,不同之处是StringBuffer属于可变类。
String是不可变类,也就是说,String对象一旦被创建,其值将不能被改变,而StringBuffer是可变类,当对象被创建后仍然可以对其进行修改。由于String是不可变类,因此适合在需要被共享的场合来使用,而当一个字符串经常需要被修改时,在字符串被修改时会比StringBuffer多很多附加的操作,同时生成很多无用的对象,由于这些无用的对象会被垃圾回收器来回收,因此会影响程序的性能。在规模小的项目里面这个影响很小,但是在一个规模大的项目里面,这会对程序的运行效率带来很大的影响。
String与StringBuffer的另外一个区别在于实例化String时,可以利用构造函数(String s1=new String("world"))的方式来对其进行初始化,也可以赋值(String s="Hello")的方式来初始化,而StringBuffer只能使其构造函数(StringBUffer s=new StringBuffer("Hello"))的方法来初始化。
StringBuilder也是可以被修改的字符串,它与StringBuffer类似,都是字符串缓冲区,但StringBuilder不是线程安全的,如果只是在单线程中使用字符缓冲区,那么StringBuilder的效率会更高些。因此在只有单线程访问时可以使用StringBuilder,当有多个线程访问时,最好使用线程安全的StringBuffer。因为StringBuffer必要时可以对这些方法进行同步,所以任意特定实例上的所有操作就好像是以串行顺序发生的,该顺序与所涉及的每个线程进行的方法调用顺序一致。
在执行效率方面,StringBuilder最高,StringBuffer次之,String最低,鉴于这一情况,一般而言,如果要操作的数据量比较小,应优先使用String类;如果是在单线程下操作大量数据,应优先使用StringBuilder类;如果是在多线程下操作大量数据,应优先考虑StringBuffer类。

60.Java中数组是不是对象

数组是指具有相同类型的集合,它们一般具有固定的长度,并且在内存中占据连续的空间。在C/C++语言中,数据名只是一个指针,这个指针指向了数组的首元素,即没有属性也没有方法可以调用,而在Java语言中,数组不仅有其自己的属性(例如length属性),也有一些方法可以被调用(例如clone方法)。由于对象的特点是封装了一些数据,同时提供了一些属性和方法,从这个角度来讲,数组是对象。每个数组类型都有其对应的类型,可以通过instanceof来判断数据的类型。

61.数组的初始化方式有哪几种

在Java语言中,一维数组的声明方式为:type arrayName[]或type[] arrayName
其中,type既可以是基本的数据类型,也可以是类,arrayName表示数组的名字,[]用来表示这个变量的类型为一维数组。与C/C++语言不同的是,在Java语言中,数组被创建后会根据数据存放的数据类型初始化成对应的初始值(例如,int类型会初始化为0,对象会初始化为null)。另外一个不同之处的是Java数组在定义时,并不会给数组元素分配存储空间,因此[]中不需要指定数组的长度,对于使用上面方式定义的数组在使用时还必须为之分配空间,分配方法为:
arrayName=new type[arraySize];//arraySize表示数组的长度

62.下列数组的定义中,哪三条是正确的?

A.public int a []		B.static int [] a			C.public [] int a
D.private int a[3]		E.private int [3] a []		F.public final int [] a

答案:A、B、F

63.下列数组定义及赋值中,错误的是()

A.int intArray[];
B.intArray=new int [3];intArray[1]=1;intArray[2]=2;intArrat[3]=3;
C.int a[]={1,2,3,4,5};
D.int[][] a=new int[2][];a[0]=new int[3];a[1]=new int[3];

答案:B
B中对数组的访问越界了。数据的大小为3,数组第一个元素为intArray[0],最后一个元素为intArray[2]。

64.下列说法中,错误的有()

A.数组是一种对象		B.数组属于一种原生类
C.int number[]={31,23,33,43,35,63}		D.数组的大小可以任意改变

答案:B、D。
原生类指未被实例化的类,数组一般是指实例化、被分配空间的类,所以不属于原生类。

65.下列语句中,创建了一个数组实例的是()

A.int [] ia=new int[15];				B.float fa=new float[20];
C.char[] ca="Some String";				D.int ia[][]={4,5,6}{1,2,3};

答案:A

66.finally块中的代码什么时候被执行

在Java语言的异常处理中,finally块的作用就是为了保证无论出现什么情况,finally块里的代码一定会被执行。由于程序执行return就意味着结束对当前函数的调用并跳出这个函数体,因此任何语句要执行都只能在return前执行(除非碰到exit函数),因此finally块里的代码也是在return语句将会覆盖别处的return语句,最终返回到调用着者那里的是finally中return的值。

67.下面程序的运行结果是什么?

	public class Foo{
		public static void main(){
			try{
				return;
			}
			finally{
				System.out.println("Finally");
			}
		}
	}
A.Finally		B.编译失败		C.代码正常运行但没有任何输出			D.运行时抛出异常

答案:A

68.异常处理的原理是什么

异常是指程序运行时(非编译时)所发生的非正常情况或错误,当程序违反了语义规则时,JVM就会将出现的错误表示为一个异常并抛出。这个异常可以在catch程序块中进行捕获,然后进行处理。而异常处理的目的则是为了提高程序的安全性与鲁棒性。
Java语言把异常当作对象来处理,并定义了一个基类(java.lang.Throwable)作为所有异常的父类。在Java API中,已经定义了许多异常类,这些异常类分为Error(错误)和Exception(异常)两大类。
违反语义规则包含两种情况:一种是Java类库内置的语义检查,例如当数组下标越界时,会引发IndexOutOfBoundsException,当访问null的对象时,会引发NullPointerException;另一种情况是Java允许开发人员扩展这种语义检查,开发人员可以创建自己的异常类(所有异常都是Java.lang.Thowable),并自由选择在何时用throw关键字抛出异常。

69.下列异常中,能使用throw抛出的是()

A.Error		B.Event		C.Object		D,Throwable		E.Exception

答案:A、D、E、F。其中Throwable为异常处理的基类,Error、Exception和RuntimeException都是Throwable的子类,因此都能使用throw抛出。

70.运行时异常和普通异常有什么区别

Java提供了两种错误的异常类,分别为Error和Exception,且他们拥有共同的父类----Throwable。
Error表示程序在运行期间出现了非常严重的错误,并且该错误是不可恢复的,由于这属于JVM层次的严重错误,因此这种错误是会导致程序终止执行的。此外,编译器不会检查Error是否被处理,因此正在程序中不推荐去捕获Error类型的的异常,主要原因是运行时异常多是由于逻辑错误导致的,属于应该解决的错误,也就是说,一个正确的程序是不应该存在Error的。OutOfMemoryError、ThreadDeach等都属于错误。当这些异常发生时,JVM一般会选择将线程终止。
Exception表示可恢复的异常是编译器可以捕捉到的。它包含两种类型:检查异常(checked exception)和运行时异常(runtime exception)。
(1)检查异常
检查异常是在程序中最经常碰到的异常。所以继承自Exception并且不是运行时异常的异常都是检查异常,比如最常见的IO异常和SQL异常。这种异常都发生在编译阶段,Java编译器强制程序去捕获此类型的异常,即把可能会出现这些异常的代码放到try块中,把对异常处理的代码放到catch块中。这种异常一般在啊如下几种情况中使用:
1)异常的发生并不会导致程序出错,进行处理后可以继续执行后续的操作,例如,当连接数据库失败后,可以重新连接后进行后续操作。
2)程序依赖于不可靠的外部条件,例如系统IO。
(2)运行时异常不同于检查异常,编译器没有强制对其进行捕获并处理。如果不对这种异常进行处理,当出现这种异常时,会由JVM来处理,例如NullPointerException异常,它就是运行时异常。在Java语言中,最常见的运行时异常包含NullPointerException(空指针异常)、ClassCastException(类型转换异常)、ArrayIndexOutOfBoundsException(数组越界异常)、ArrayStoreException(数组存储异常)、BufferOverflowException(缓冲区溢出异常)、ArithmeticException(算术异常)等。
出现运行时异常后,系统会把异常一直往上层抛出,直到遇到处理代码为止。若没有处理块,则抛到最上层;如果是多线程就用Thread,run()方法抛出,如果是单线程,就用main()方法抛出。抛出之后,如果是线程,那么这个线程也就退出了。如果是主程序抛出的异常,那么整个程序也就退出了。所以,如果不对运行时的异常进行处理,后果是非常严重的,一旦发生,要么是线程中止,要么是主程序终止。
在使用异常处理时,还需要注意以下几个问题:
1)Java异常处理用到了多态的概念,如果在异常处理过程中,先捕获了基类,然后再捕获子类,那么捕获子类的代码块将永远不会被执行。因此,在进行异常捕获时,正确的写法是:先捕获子类,再捕获基类的异常信息,实例如下:

正确的写法								错误的写法
try{									try{
	//access db code						//access db code
}catch(SQLException el){				}catch(Exception e1){
	//deal with this exception				//deal with this exception
}catch(Exception e2){					}catch(SQLException e2){
}										}

2)尽早抛出异常,同时对捕获的异常进行处理,或者从错误中恢复,或者让程序继续执行。对捕获的异常不进行任何处理是一个非常不好的习惯,这样将非常不利于调试。但也不是抛出异常越多越好,对弈有些异常类型,例如运行时异常,实际上根本不必处理。
3)可以根据实际的需求自定义异常类,这些自定义的异常类只要继承自Exception类即可。
4)异常能处理就处理,不能处理就抛出。对于一般异常,如果不能进行行之有效的处理,最好转换为运行时异常抛出。对于最终没有处理的异常,JVM会进行处理。

71.下面程序能否编译通过如果ArithmeticException换成IOException呢?

public class ExceptionTypeTest{
	public void doSomething() throws ArithmeticException{
		System.out.println();
	}
	public static void main(){
		ExceptionTypeTest ett=new ExceptionTypeTest();
		ett.doSomething();
	}
}

答案:能。由于ArithmeticException属于运行时异常,编译器没有强制对其进行捕获并处理,因此编译可以通过。但是如果换成IOException后,由于IOException属于检查异常,编译器强制去捕获此类型的异常,因此如果不对异常进行捕获将会有编译错误。

72.异常包含下列哪些内容?()

A.程序中的语法错误				B,程序的编译错误
C.程序执行过程中遇到的事先没有预料的情况
D.程序事先定义好的可能出现的意外情况

答案:C。

73.下列关于异常的说法中,正确的是()。

A.一旦出现异常,程序运行就终止了
B.如果一个方法申明将抛出某个异常,它就必须真的抛出那个异常
C.在catch'字句中匹配异常是一种精确匹配
D.可能抛出系统异常的方法是不需要申明异常的

答案:D。

74.Java有几种类型的流?

答案:常见的流有两种,分别为字节流与字符流。其中,字节流继承于InputStream与OutputStream,字符流继承于Reader与Writer。在java.io包中还有许多其他的流,流的作用主要是为了改善程序性能并且使用方便。

75.管理文件和目录的类是什么

对文件或目录进行管理与操作在编程中有着非常重要的作用,Java提供了一个非常重要的类(File)来管理文件和文件夹,通过类不仅能够查看文件或目录的属性,而且还可以实现对文件或目录的创建、删除与重命名等操作。

					File类常用的方法
方法											作用
File(String pathname)						根据指定的路径创建一个File对象
createNewFile()								若目录或文件存在,则返回false,否则创建文件或文件夹
delete()									删除文件或文件夹
isFile()									判断这个对象表示的是否是文件
isDirectory()								判断这个对象表示的是否是文件夹
listFiles()									若对象代表目录,则返回目录中所有文件的File对象
mkdir()										根据当前对象指定的路径创建目录
exists()									判断对象对应的文件是否存在

76.如何列出某个目录下的所有目录和文件?

答案:假设目录"C:\\testDir1"下有两个文件夹(dirl和dir2)和一个文件file1.txt,实现代码如下:

import java.io.File; 
public class Test{
	public static void main(){
		File file=new File("C:\\testDir");
		//判断目录是否存在
		if(!file.exists()){
			System.out.println("dirctory is empty");
			return;
		}
		File[] fileList=file.listFiles();
		for(int i=0;i<fileList.length;i++){
			//判断是否为目录
			if(fileList[i].isDirectory()){
				System.out.println("dirctory is: "+fileList[i].getName());
			}else{
				System.out.println("file is:" +fileList[i]+getName());
			}
		}
	}
}

程序运行结果:
dirctory is : dirl
dirctory is: dir2
file is:file1.txt

77.Java Socket是什么

网络上两个程序通过一个双向的通信连接实现数据的交换,这个双向链路的一端称为一个Socket。Socket也称为套接字,可以用来实现不同虚假机或不同计算机之间的通信。在Java语言中,Socket可以分为两种类型:面向连接的Socket通信协议(TCP,Transmission Control Protocol,传输控制协议)和面向无连接的Socket通信协议(UDP,User Datagram Protocol,用户数据报协议)。任何一个Socket都是由IP地址和端口号唯一确定的。
基于TCP的通信过程如下:首先,Server(服务器)端Listen(监听)指定的某个端口(建议使用大于1024的端口)是否有连接请求;其次,Cilent(客户)端向Server端发出Connect(连接)请求;最后,Server端向Client端发回Accept(接受)消息。一个连接就建立起来了,会话随即产生。Server端和Cilent都可以通过Send、Write等方法与对方通信。
Socket的生命周期可以分为3个阶段:打开Socket、使用Socket收发数据和关闭Socket。在Java语言中,可以使用ServerSocket来作为服务器端,Socket作为客户端来实现网络通信。

78.用Socket实现客户端和服务端的通信,要求客户端发送数据后能够回显相同的数据。

Server.java
import java.net.*;
import java.io.*;
class Server{
	public static void main(){
		BuffferedReader br=null;
		PrintWriter pw=null;
		try{
			ServerSocket server=new ServerSocket(2000);
			Socket scoket=server.accept();
			//获取输入流
			br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
			//获取输出流
			pw=new PrintWriter(socket.getOutputStream(),true);
			String s=br.readLine();//获取接收的数据
			pw.println(s);//发送相同的数据给客户端
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			try{
				br.close();
				pw.close();
			}catch(Exception e){
			}
		}
	}
}
Client.java
import java.net.*;
import java.io.*;
class Client{
	public static void main(String[] args){
		BufferedReader br=null;
		PrintWriter pw=null;
		try{
			Socket socket=new Socket("localhost",2000);
			//获取输入流与输出流
			br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
			pw=new PrintWriter(socket.getOutputStream(),true);
			//向服务器发送数据
			pw.println("Hello");
			String s=null;
			while(true){
				s = br.readLine();
				if(s!=null)
					break;
			}
			System.out.println(s);
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			try{
				br.close();
				pw.close();
			}catch(Exception e){
			}
		}
	}
}

最后启动服务器端程序,然后运行客户端程序,客户端将会把从服务端转发过来的"Hello"打印出来

79.序列化有什么特点?

1)如果一个类能被序列化,那么它的子类也能够被序列化。
2)由于static(静态)代表类的成员,transient(Java语言关键字,如果用transient声明一个实例变量,当对象存储时,它的值不需要维持。)代表对象的临时数据,因此被声明为这种类型的数据成员是不能被序列化的。

80.由于序列化的使用会影响系统的性能,因此如果不是必须使用序列化,应尽可能不要使用序列化。那么在什么情况下需要使用序列化呢?

1)需要通过网络来发送对象,或对象的状态需要被持久化到数据库或文件中。
2)序列化能实现深复制,既可以复制引用的对象。

81.创建一个如下的DataObject:DataObject object=new DataObject();object.setWord(“123”);object.setI(2);将此对象序列化文件,并在另一个JVM中读取文件,进行反序列化,请问此时读出的DataObject对象中的word和i的值分别是()

import java.io.Serializable;
public class DataObject implements Serializable{
	private static int i=0;
	private String word="";
	public static void setI(int i){
		DataObject.i=i;
	}
	public void setWord(String word){
		this.word=word;
	}
}
A."",0			B."",2			C."123",2			D."123",0

答案:D。
Java在序列化时不会实例化static变量,因此上述代码只实例化了word,而没有实例化i。在反序列化时只能读取word的值,i为默认值。

82.为什么说Java是平台独立性语言

平台独立性是指可以在一个平台编写和编译程序,而在其他平台运行。保证Java具有平台独立性的机制为"中间码"和"Java虚拟机(Java Virtual Machine,JVM)"。Java程序被编译后不是生成能在硬件平台下可执行的代码,而是生成了一个"中间码"。不同的硬件平台上会安装不同的JVM,由JVM来复制把"中间码"翻译成硬件平台能执行的代码。由此可以看出JVM不具有平台独立性,而是与硬件平台相关的。
解释执行过程分三步进行:代码的装入、代码的校验和代码的执行。装入代码的工作由"类装载器"完成。被装入的代码由字节码校验器进行检查。
Java字节码的执行也分为两种方式:即时编译方式与解释执行方式,即时编译方式指的是解释器先将字节码编译成机器码,然后再执行该机器码。通常采用的是解释执行方式。
而在C/C++语言中,编译后的代码只能再特定的硬件上执行,换个硬件平台这些代码就无法执行了,从而也导致了C/C++没有跨平台的特性。但C/C++有更高的执行效率。

83.一个Java程序运行从上到下的环境次序是()

A.操作系统、Java程序、JRE/JVM、硬件
B.JRE/JVM、Java程序、硬件、操作系统
C.Java程序、JRE/JVM、操作系统、硬件
D.Java程序、操作系统、JRE/JVM、硬件

答案:C

84.下列说法中,正确的是()。

A.Java程序经编译后会产生机器码。
B.Java程序经编译后会产生字节码
C.Java程序经编译会产生DLL
D.以上都不正确

答案:B。
.java文件会被javac指令编译为.class后缀的字节码文件,再由JVM执行。

85.Java平台与其他语言平台有哪些区别?

Java平台是一个纯软件的平台,这个平台可以运行在一些基于硬件的平台(例如Linux、Windows等)之上。Java平台主要包含两个模板:JVM与Java API(Application Program Interface,应用程序接口)。
JVM是一个虚拟出来的计算机,用来把Java编译生成的中间代码转换为机器可以识别的编码并运行。它有自己完善的硬件架构,例如处理器、堆栈顿、寄存器等、还具有相应的指令系统,它屏蔽了与具体操作系统平台相关的信息,使得Java程序只需生成在JVM上运行的目标代码(即字节码),就可以在多种平台上不加修饰地顺序运行。每当一个Java程序运行时,都会有一个对应的JVM实例,只有当程序运行结束后,这个JVM才会退出。JVM实例通过调用类的main()方法来启动一个Java程序,而这个main()方法必须是公有的、静态的且返回值为void的方法,该方法接受一个字符串数组的参数,只有同时满足这些条件才可以作为策划那个小的入口方法。
Java API是Java为了方便开发人员进行开发而设计的,它提供了许多非常有用的接口,这些接口也是用Java语言编写的,并且运行在JVM上。

86.JVM加载class文件的原理是什么

Java语言是一种具有动态的解释型语言,类(class)只有被加载到JVM中后才能运行。当运行指定程序时,JVM会将编译生成的 .class文件按照需求和一定的规则加载到内存中,并组织成为一个完整的Java应用程序。这个加载过程是由类加载器来完成的,具体来说,就是由ClassLoader和它的子类来实现。类加载器本身也是一个类,其实质是把类文件从硬盘读取到内存中。
类的加载方式分为隐式加载与显式加载两种。隐式加载指的是程序在使用new等方式创建对象时,会隐式地调用类的加载器把对应的类加载到JVM中。显式加载指的是通过直接调用class.forName()方法来把所需的类加载到JVM中。
任何一个工程项目都是由许多个类组成的,当程序启动时,只把需要的类加载到JVM中,其他类只有被使用到的时候才会被加载,采用这种方法,一方面可以加快加载速度,另外一方面可以节约程序运行过程种对内存的开销。此外,在Java语言中,每个类或接口都对应一个.class文件,这些文件可以被看成一个个可以被动态加载的单元,因此当只有部分类被修改时,只需要重新编译变化的类即可,而不需要重新编译所有文件,因此加快了编译速度。

87.类加载的过程分为以下3步:

1)装载。根据查找路径找到相应的class文件,然后导入。
2)链接。链接又可以分为3个小的步骤,具体如下。
检查。检查待加载的class文件的正确性。
准备。给类中的静态变量分配存储空间。
解析。将符号引用转换成直接引用(这一步是可选的)。
3)初始化。对静态变量和静态代码块执行初始化工作。

88.什么是GC

在Java语言中,垃圾回收(Garbage Collection,GC)是一个非常重要的概念,它的主要作用是回收程序中不再使用的内存。在使用C/C++语言进行程序开发时,开发人员必须非常仔细地管理好内存的分配与释放,如果忘记或者错误地释放内存往往会导致程序运行不正常甚至是程序崩溃。为了减轻开发人员的工作,同时增加系统的安全性与稳定性,Java语言提供了垃圾回收器来自动检测对象的作用域,可自动地把不再被使用的存储空间释放掉。具体而言,垃圾回收器来自动检测对象的作用域,可自动地把不再被使用的存储空间释放掉。具体而言,垃圾回收器要负责完成3项任务:分配内存、确保被引用对象的内存不被错误地回收以及回收不再被引用的对象的内存空间。

89.GC的作用?

垃圾回收器的存在一方面把开发人员从释放内存的复杂工作解脱出来,提高了开发人员的生产效率;另一方面,对开发人员屏蔽了释放内存的方法,可以避免因开发人员错误地操作内存而导致应用程序的崩溃,保证了程序的稳定性。但是,垃圾回收也带来了问题,为了实现垃圾回收,垃圾回收器必须跟踪内存的使用情况,释放没用的对象,在完成内存的释放后还需要处理堆中的碎片,这些操作必定会增加JVM的负担,从而降低程序的执行效率。

90.当Float对象在第2行被创建后,什么时候能够被垃圾回收?

1	public Object m(){
2		Object o=new Float(3.14F);
3		Object[] oa=new Object[1];
4		oa[0]=o;
5		o=null;
6		oa[0]=null;
7		print`retuen 0`;
8	}
A.4行以后		B.5行以后		C.6行以后		D.7行以后

答案:C。
在第6行后不再有对象引用Float对象了,因此能够被垃圾回收。

91.下列关于垃圾回收的说法中,正确的是()

A.一旦一个对象成为垃圾,就立刻被回收掉
B.对象空间被回收掉之后,会执行该对象的finalize方法
C.finalize方法和C++的析构函数完全是一回事情
D.一个对象成为垃圾是因为不再有引用指着它,但是线程并非如此

答案:D。成为垃圾的对象,只有在下次垃圾回收器运行时才会被回收,而不是马上被清理,因此选项A错误。finalize方法是在对象空间被回收前调用的,因此选项B错误。在C++语言中,调用了析构函数后,对象一定会被销毁,而Java语言调用了finalize方法,垃圾却不一定会被回收,因此finalize方法与C++的析构函数是不同的,所以选项C也不正确。对于D,当一个对戏那个不再被引用后就成为垃圾可以被回收,但是线程就算没有被引用也可以独立运行的,因此与对象不同。

92.是否可以主动通知JVM进行垃圾回收?

由于垃圾回收器的存在,Java语言本身没有给开发人员提供显示释放已分配内存的方法,也就是说,开发人员不能实时地调用垃圾回收器对某个对象或所有对象进行垃圾回收。但开发人员却可以通过System.gc()方法来"通知"垃圾回收器运行,当然,JVM也并不会保证垃圾回收器马上就会运行。由于System.gc()方法的执行会停止所有响应,去检查内存中是否有可回收的对象,这会对程序的正常运行以及性能造成极大的威胁,因此实际编程时,不推荐频繁使用这一方法。

93.Java是否存在内存泄漏问题

答案:内存泄漏是指一个不再被程序使用的对象或变量还在内存中占有存储空间。在C/C++语言中,内存的分配与释放是由开发人员来负责的,如果开发人员忘记释放已分配的内存就会造成内存泄漏。而在Java语言中引进了垃圾回收机制,由垃圾回收器负责回收不再使用的对象,既然有垃圾回收器来负责回收垃圾,那么是否还会存在内存泄漏的问题呢?
其实,在Java语言中,判断一个内存空间是否符合垃圾回收的标准有两个:第一,给对象赋予了空值null,以后在没有被使用过;第二,给对象赋予了新值,重新分配了内存空间。一般来讲,内存泄漏主要有两种情况:一种是在堆中申请的空间没有被释放;二是对象已不再被使用,但还仍然在内存中保留着。垃圾回收机制的引入可以有效地解决第一种情况;而对于第二种情况,垃圾回收机制则无法保证不再使用的对象会被释放。因此,Java语言中的内存泄漏主要指的是第二种情况。

94.引起内存泄漏的原因有哪些?

1)静态集合类,例如HashMap和Vector。如果这些容器为静态的,由于它们的生命周期与程序一致,那么容器中的对象在程序结束之前将不能被释放,从而造成内存泄漏,如上例所示。
2)各种连接,例如数据库连接、网络联接以及IO连接等。在对数据库进行操作的过程种,首先需要建立与数据库的连接,当不再使用时,需要调用close方法来释放与数据库的连接。只有连接被关闭后,垃圾回收器才会回收对应的对象。否则,如果是访问数据库的过程中,对Connection、Statement或ResultSet不显式地关闭,将会造成大量的对象无法被回收,从而引起内存泄漏。
3)监听器。在Java语言中,往往会使用到监听器。通常一个应用中会用到多个监听器,但在释放对象的同时往往没有相应地删除监听器,这也可能导致内存泄漏。
4)变量不合理的作用域。一般而言,如果一个变量定义的作用范围大于其使用范围,很可能会造成内存泄漏,另一方面如果没有及时地把对象设置为null,很有可能会导致内存泄漏的发生。
5)单例模式可能会造成内存泄漏。

95.Java的堆和栈有什么区别

在Java语言中,堆与栈都是内存中存放数据的地方。变量分为基本数据类型和引用类型,基本数据类型的变量(例如int、short、long、byte、float、double、boolean以及char等)以及对象的引用变量,其内存都分配在栈上,变量出了作用域就会自动释放,而引用类型的变量,其内存分配在堆上或者常量池(例如字符串常量和基本数据类型常量)中,需要通过new等方式进行创建。
具体而言,栈内存主要用来存放基本数据类型与引用变量。栈内存的管理是通过压栈和弹栈操作来完成的,以栈帧为基本单位来管理程序的调用关系,每当有函数调用时,都会通过压栈方式创建新的栈帧,每当函数调用结束后都会通过弹栈的方式释放栈帧。
堆内存用来存放运行时创建的对象。一般来讲,通过new关键字创建出来的对象都存放在堆内存中。由于JVM是基于堆栈的虚拟机,而每个Java程序都运行在一个单独的JVM,实例上,每一个实例唯一对应一个堆,一个Java程序内的多个线程也就运行在同一个JVM,实例上,因此这些线程之间会共享堆内容,鉴于此,多线程在访问堆中的数据时需要对数据同步。
从堆和栈的功能以及作用来比较,堆主要用来存放对象的,栈主要用来执行程序的。相较于堆,栈的存取速度更快,但栈的大小和生存期必须是确定的,因此缺乏一定的灵活性。而堆却可以在运行时动态地分配内存,生存期不用提前告诉编译器,但这也导致了其存取速度的缓慢。

96.Java Collections框架是什么

Java Collections 框架中包含了大量集合接口以及这些接口的实现类和操作它们的算法(例如排序、查找、反转、替换、复制、取最小元素、取最大元素等),具体而言,主要提供了List(列表)、Queue(队列)、Set(集合)、Stack(栈)和Map(映射表,用于存放键值对)等数据结构。其中,List、Queue、Set、Stack都继承自Collection接口。
Collection是整个集合框架的基础,它里面存储一组对象,表示不同类型的Collections,它的作用只是提供维护一组对象的基本接口而已。

97.简单的介绍下Set、List和Map接口.

1)Set表示数学意义上的集合概念。其最主要的特点是集合中的元素不能重复,因此存在Set的每个元素都必须定义equals()方法来确保对象的唯一性。该接口有两个实现类:HashSet和TreeSet。其中TreeSet实现了SortedSet接口,因此TreeSet容器中的元素是有序的。
2)List又称为有序的Collection。它按对象进入的顺序保存对象,所以它能对列表的每个元素的插入和删除位置进行精确的控制。同时,它可以保存重复的对象。LinkedList、ArrayList和Vector都实现了List接口。
3)Map提供了一个从键映射到值得数据结构。它用于保存键值对,其中值可以重复,但键是唯一的,不能重复。Java类库中有多个实现该接口的类:HashMap、TreeMap、LinkedHashMap、WeakHashMap和IdentityHashMap。虽然它们都实现可相同的接口,但执行效率却不是完全相同的。具体而言,HashMap是基于散列表实现的,采用对象的HashCode可以进行快速查询。LinkedHashMap采用列表来维护内部的顺序。TreeMap基于红黑树的数据结构来实现的,内部元素是按需排列的。

98.下面哪种创建Map集合的方式是正确的?

A.Map m=new Map();
B.Map m=new Map(init capacity,increment capacity);
C.Map m=new Map(new Collection());
D.Map是接口,所以不能实例化。

答案:D。
由于Map是一个接口,因此不能直接实例化Map的对象,但是可以实例化实现Map接口的类的对象,例如Map m=new HashMap()。

99.什么是迭代器

迭代器(Iterator)是一个对象,它的工作是遍历并选择序列化中的对象,它提供了一种访问一个容器(container)对象中的各个元素,而又不必暴露该对象内部细节的方法。通过迭代器,开发人员不需要了解容器底层的结构,就可以实现对容器的遍历。由于创建迭代器的代价小,因此迭代器通常被称为轻量级的容器。

100.迭代器的使用有那些注意的事项

迭代器的使用主要有以下3个方面的注意事项:
1)使用容器的itertor()方法返回一个Iterator,然后通过Iterator,然后通过Iterator的next()方法返回第一个元素。
2)使用Iterator的hasNext()方法判断容器中是否还有元素,如果有,可以使用next()方法获取下一个元素。
3)可以通过remove()方法删除迭代器返回的元素。
Iterator支持派生的兄弟成员。ListIterator只存在于List中,支持在迭代器期间向List中添加或删除元素,并且可以在List中双向滚动。
Iterator支持派生的兄弟成员。ListIterator只存在于List中,支持在迭代期间向List中添加或删除元素,并且可以在List中双向滚动。

101.多线程访问容器的过程中抛出ConcurrentModificationException异常又该怎么解决呢?

1)在JDK1.5版本引入了线程安全的容器,比如ConcurrentHashMap和CopyOnWriteArrayList等。可以使用这些线程安全 的容器来代替非线程安全的容器。
2)在使用迭代器遍历容器时对容器的操作放到synchronized代码块中,但是当引用程序并发程序比较高时,这会严重影响程序的性能。

102.Iterator与ListIterator有什么区别?

Iterator只能正向遍历集合,适用于获取移除元素。ListIerator继承自Iterator,专门针对List,可以从两个方向来遍历List,同时支持元素的修改。

103.ArrayList、Vector和LinkedList有什么区别

ArrayList、Vector、LinkedList类均为在java.util包中,均为可伸缩数组,即可以动态改变长度的数组。
ArrayList和Vector都是基于存储元素的Object[]array来实现的,它们会在内存中开辟一块连续的空间来存储,由于数据存储是连续的,因此,它们支持用序号(下标)来访问元素,同时索引数据的速度比较快。但是在插入元素时需要移动容器中的元素,所以对数据的插入操作执行得比较慢。ArrayList和Vector都有一个初始化的容量的大小,当里面存储的元素超过这个大小时就需要动态地扩充它们的存储空间。为了提高程序的效率,酶促扩充容量,不是简单地扩充一个存储单元,而是一次增加多个存储单元。Vector默认扩充为原来的1.5倍(没有提供方法来设置空间扩充的方法)。
ArrayList与Vector最大的区别就是synchronization(同步)的使用,没有一个ArrayList的方法是同步的,而Vector的绝大多数方法(例如add、insert、remove、set、equals、hashcode等)都是直接或者间接同步的,所以Vector是线程安全的,ArrayList不是线程安全的。正是由于Vector提供了线程安全的机制,其性能上也要略逊于ArrayList。
LinkedList是采用双向列表来实现的,对数据的索引需要从列表头开始遍历,因此用于随机访问则效率比较低,但是插入元素时不需要对数据进行移动,因此插入效率较高。同时LinkedList是非线程安全的容器。
那么,在实际使用时,如何从这几种容器中选择合适的使用呢?当对数据的主要操作作为所以或只在集合的末端增加、删除元素时,使用ArrayList或Vector效率比较高;当对数据的操作主要为指定位置的插入或删除操作时,使用LinkedList效率 比较高;当在多线程中使用容器时(即多个线程会同时访问该容器),选用Vector较为安全。

104.若线性表最常用的操作是存取第i个元素及其前趋的值,则采用()存储方式节省时间。

A.单链表				B.双链表				C.单循环链表			D.顺序表

D。顺序适合在随机访问的场合使用,访问时间复杂度为O(1),而列表的随机访问操作的时间复杂度为O(n)。

105.对于import java.util包,下列说法中,错误的是()

A.Vector类属于java,util包			B.Vector类放在.../java/util/目录下
B.Vector 类放在java.util文件中		D.Vector类是Sun公司的产品

答案:C。java.util是包名,实质上是一个目录结构。

106.HashMap、Hashtable、TreeMap和WeakHashMap有哪些区别

Java为数据结构中的映射定义了一个接口 java.util.Map,它包括3个实现类:HashMap、Hashtable和TreeMap。Map是用来存储键值对的数据结构,在数组中通过数组下标来对其内容索引的,而在Map中,则是通过对象来进行索引,用来索引的对象叫做key,其对应的对象叫做value。
HashMap是一个最常用的Map,它根据键的HashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度。由于HashMap与Hashtable都采用了hash法进行索引,因此二者具有许多相似之处,它们主要有如下的一些区别:
1)HashMap是Hashtable的轻量级实现(非线程安全的实现),它们都完成了Map接口,主要区别在于HashMap允许空(null)键值(key)(但需要注意,最多只允许一条记录的键为null,不允许多条记录的值为null),而Hashtable不允许。
2)HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey,因为contains方法容易让人引起误解。Hashtable继承自Dictionary类,而HashMap是Java 1.2 引进的Map interface的一个实现。
3)Hashtable的方法是线程安全的,而HahsMap不支持线程的同步,所以它不是线程安全的。在多个线程访问Hashtable时,不需要开发人员对它进行同步,而对于HashMap,开发人员必须提供额外的同步机制。所以,就效率而言,HashMap可能高于Hashtable。
4)Hashtable使用Enumeration,HashMap使用Iterator。
5)Hashtbale和HashMap采用的hash/rehash算法都几乎一样,所以性能不会有很大的差异。
6)在Hashtable中,hash数组默认大小是11,增加的方式是old*2+1。在HashMap中,hash数组的默认大小是16,而且一定是2的指数。
7)hash值得使用不同,Hashtable直接使用对象的hashCode。
以上3种类型,使用做多的是HashMap。HashMap里面存入的键值对在取出时没有固定的顺序,是随机的。一般而言,在Map中插入、删除和定位元素,HashMap是最好的选择。由于TreeMap实现了SortMap接口,能够把它保存的记录根据键排序,因此,取出来的是排序后的键值对,如果需要按自然顺序或自定义顺序遍历键,那么TreeMap会更好。LinkedHashMap是HashMap的一个子类,如果需要输出的顺序和输入的相同,那么用LinkedHashMap可以实现,它还可以按读取顺序来排列。
WeakHashMap与HashMap类似,二者的不同之处在于WeakHashMap中key采用的是"弱引用"的方式,当HashMap中的key没有被外部引用时,只有在这个key从HashMap中删除后,才可以被垃圾回收器回收。

107.在Hashtable上下文中,同步指的是什么?

答案:同步意味着在同一个时间点只能有一个线程可以修改hash表,任何线程在执行Hashtable的更新操作前都需要获取对象锁,其他线程则等待锁的释放。

108.如何实现HashMap的同步?

HashMap通过Map m=Collections.synchronizedMap(new HashMap())来达到同步的效果。具体而言,该方法返回一个同步的Map,该Map封装了底层的HashMap的所有方法,使得底层的HashMap即使是在多线程的环境中也是安全的。

109.用自定义类型作为HashMap或Hashtable的key需要注意哪些问题?

hashMap与Hashtable是用来存放键值对的一种容器,在使用这两个容器时有一个限制:不能用来存储重复的键。也就是说,每个键只能唯一映射一个值,当有重复的键时,不会创建新的映射关系,而会使用先前的键值。

110.开发者在使用自定义类作为HashMap的key时,需要注意以下几个问题?

1)如果想根据对象的相关属性来自定义对象是否相等的逻辑,此时就需要重写equals()方法,一旦重写了equals()方法,那么就必须重写hashCode()方法。
2)当自定义类的多项作为HashMap(Hashtable)的key时,最好把这个类设计为不可变类。
3)从HashMap的工作原理可以看出,如果两个对象相等,那么这两个对象有着相同的hashCode,反之则不成立。

111.Collection和Collection有什么区别?

Collection是一个集合接口。它提供了对集合对象进行基本操作的通用接口方法。实现该接口的类主要是List和Set,该接口的设计目标是为各种具体的集合提供最大化的统一的操作方式。
Collections是针对集合类的一个包装类,它提供了一系列静态方法以实现对各种集合的搜索、排序、线程安全化等操作,其中大多数方法都是用来处理线性表。Collections类不能实例化,如同一个工具类,服务于Collection框架。若在使用Collections类的方法时,对应的collectin的对象为null,则这些方法都会抛出NullPointerException。

112.什么是线程?它与进程有什么区别?为什么要是用多线程?

1)线程是指程序在执行过程中,能够执行程序代码的一个执行单元。在Java语言中,线程有四种状态:运行、就绪、挂起和结束。
2)进程是指一段正在执行的程序。而线程有时也被称为轻量级进程,它是程序执行的最小单元,一个进程可以拥有多个线程,各个线程之间共享程序的内存空间(代码段、数据段和堆空间)及一些进程级的资源(例如打开的文件),但是各个线程拥有自己的栈空间。
3)在操作级别上,程序的执行都是以进程为单位的,而每一个进程中通常都会有多个线程互不影响地并发执行,那么为什么要使用多线程呢?其实,多线程的使用为程序研发带来了巨大的便利,具体而言,有以下几个方面的内容:
使用多线程可以减少程序的响应时间。在单线程(单线程指的是程序执行过程中只有一个有效操作的序列,不同操作之间都有明确的执行先后顺序)的情况下,如果某个操作很耗时,或者陷入长时间的等待(如等待网络响应),此时程序将不会响应鼠标和键盘灯操作,使用多线程后,可以把这个好使的线程分配到一个单独的线程去执行,从而使程序具备了更好的交互性。
与进程相比,线程的创建和切换开销更小。由于启动一个新的线程必须给这个线程分配独立的地址空间,建立许多数据结构来维护线程代码段、数据段等信息,而运行于同一进程内的线程共享代码段、数据段,线程的启动或切换的开销比进程要少很多。同时多线程在数据共享方面效率非常高。
多CPU或多核计算机本身就具有执行多线程的能力,如果使用单个线程,将无法重复利用计算机资源,造成资源的巨大浪费。因此在多CPU计算机上使用多线程能提高CPU的利用率。
使用多线程能简化程序的结构,使程序便于理解和维护。一个非常复杂的进程可以分成多个线程来执行。

113.同步和异步的区别

在多线程的环境中,经常会碰到数据的共享问题,即当多个线程需要访问同一资源时,它们需要以某种顺序来确保该资源在某一时刻只能被一个线程使用,否则,程序的运行结果将会是不可预料的,在这种情况下就必须对数据进行同步,例如多个线程同时对同一数据进行写操作,即当线程A需要使用某个资源时,如果这个资源正在被线程B使用,同步机制就会让线程A一直等待下去,直到线程B结束对该资源的使用后,线程A才能使用这个资源,由此可见,同步机制能够保证资源的安全。
要想实现同步操作,必须要获得每一个线程对象的锁。获得它可以保证在同一时刻只有一个线程能够进入临界区(访问互斥资源的代码块),并且在这个锁被释放之前,其他线程就不能再进入这个临界区。如果还有其他线程想要获得该对象的锁,只能进入等待队列等待。只有当拥有该对象锁的线程退出临界区时,锁才会被释放,等待队列中的优先级最高的线程才能获得该锁,从而进入共享代码区。
Java语言在同步机制中提供了语言级的支持,可以通过使用synchronized关键字来实现同步,但该方法并非"万金油",它是以很大的系统开销作为代价的,有时候甚至可能造成死锁,所以,同步控制并非越多越好,要尽量`避免无谓的同步控制。实现同步的方式有两种:一种是利用同步代码来实现同步;另一种是利用同步方法来实现同步。
异步与非阻塞类似,由于每个线程都包含了运行时自身所需要的的数据或方法,因此,在进行输入输出处理时,不必关心其他进程的状态或行为,也不必等到输入输出处理完毕才返回。当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,异步能够提高程序的效率。

114.如何实现Java多线程

1)继承Thread类,重写run()方法。
Thread本质上也是实现了Runnable接口的一个实例,它代表一个线程的实例,并且,启动线程的唯一方法就是通过Thread类的start()方法。start()方法是一个native(本地)方法。它将启动一个新线程,并执行run()方法(Thread中提供的run()方法是一个空方法)。这种方式通过自定义直接extend Thread,并重写run()方法,就可以启动新线程并执行自己定义的run()方法。需要注意的是,调用start()方法后并不是立即执行多线程代码,而是使得该线程变为可运行态(Runnable)
2)实现Runnable接口,并实现该接口的run()方法。
自定义类并实现Runnable接口,实现run()方法。
创建Thread对象,用实现Runnable接口的对象作为参数实例化该Thread对象。
调用Thread的start()方法。
其实,不管是通过继承Thread类还是通过使用Runnable接口来实现多线程的方法,最终还是通过Thread的对象的API来控制线程的。
3)实现Callable接口,重写call()方法。
Callable接口实际是属于Execetor框架中的功能类,Callable接口与Runnable接口的功能类似,但提供了比Runnable更强大的功能,主要表现为以下3点:
Callable可以在任务结束后提供一个返回值,Runnable无法提供这个功能。
Callable中的call()方法可以抛出异常,而Runnable的run()方法不能抛出异常。
运行Callable可以拿到一个Futurn来监视目标线程调用call()方法的情况,当调用Future的get()方法以获得结果时,当前线程就会阻塞,直到call()方法结束返回结果。

以上三种方式,前两种方式线程执行完后都没有返回值,只有最后一种是带返回值的。当需要实现多线程时,一般推荐实现Runnable接口的方式,其原因是:首先,Thread类定义了很多方法被派生类使用或重写。但是只有run()方法是必须被重写的,在run()方法中实现这个线程的主要功能。这当然是实现Runnable接口所需的方法。其次,很多Java开发人员认为,一个类仅在他们需要被加强或修改时才会被继承。因此,如果没有必要重写Thread类中的其他方法,那么通过继承Thread的实现方式与实现Runnadle接口的效果相同,在这种情况下最好通过实现Runnable接口的方式来创建线程。

115.run()方法与start()方法有什么区别

通常,系统通过调用线程类的start()方法来启动一个线程,此时该线程处于就绪状态,而非运行状态,也就意味着这个线程可以被JVM来调度执行。在调度过程中,JVM通过线程类的run()方法来完成实际的操作,当run()方法结束后,此线程就会终止。
如果直接调用线程类的run()方法,这会被当作一个普通的函数调用,程序中仍然只有主线程这一个线程,也就是说,start方法()能够异步地调用run()方法,但是直接调用run()方法确实同步的,因此也就无法达到多线程的目的。

116.多线程同步的实现方法有哪些

当使用多线程访问同一个资源时,非常容易出现线程安全的问题(例如,当多个线程同时对一个数据进行修改时,会导致某些线程多数据的修改丢失)。因此,需要采用同步机制来解决这种问题。Java主要提供了3种实现同步机制的方法:
(1)synchronized关键字
在Java语言中,每个对象都有一盒对象锁与之相关联,该锁表明对象在任何时候只允许被一个线程所拥有,当一个线程调用对象的一段synchronized代码时,需要先获取这个锁,然后去执行相应的代码,执行结束后,释放锁。
synchronized关键字主要有两种用法(synchronized方法和synchronized块 ),此外该关键字还可以作用于静态方法、类或某个实例,但这都对程序的效率有很大的影响。
1)synchronized方法。在方法的声明前加入synchronized关键字,实例如下:

	public synchronized void mutiThreadAccess();

只要把多个线程对类需要被同步的资源的操作放到mutiThreadAccess()方法中,就能保证这个方法在同一时刻只能被一个线程访问,从而保证了多线程访问的安全性。然而,当一个方法体规模非常大时,把该方法声明为synchronized会大大影响程序的执行效率。为了提高程序的效率,Java提供了synchronized块。
2)synchronized块。synchronized块既可以把任意的代码段声明为synchronized,也可以指定上锁的对象,有非常高的灵活性。其用法如下:

synchronized(syncObject){
	//访问synObject的代码
}

(2)waiti()方法与notify()方法
当使用synchronized来修饰某个共享资源时,如果线程A1在执行synchronized代码,另外一个线程A2也要同时执行同一个对象的同一synchronized代码时,线程A2将要等到线程A1执行完成后,才能继续执行。在这种情况下可以使用wait()方法和notify()方法

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值