java面试题——基础知识部分(2)

11、静态变量和实例变量的区别?

 

在语法定义上的区别:静态变量前要加 static 关键字,而实例变量前则不加。

 

在程序运行时的区别:实例变量属于某个对象的属性,必须创建了实例对象,其中的实例变量才会被分配空间,才能使用这个实例变量。静态变量不属于某个实例对象,而是属于类,所以也称为类变量,只要程序加载了类的字节码,不用创建任何实例对象,静态变量就会被分配空间,静态变量就可以被使用了。总之,实例变量必须创建对象后才可以通过这个对象来使用,静态变量则可以直接使用类名来引用。

 

例如,对于下面的程序,无论创建多少个实例对象,永远都只分配了一个 staticVar 变量,并且每创建一个实例对象,这个 staticVar 就会加 1;但是,每创建一个实例对象,就会分配一个 instanceVar,即可能分配多个 instanceVar,并且每个 instanceVar 的值都只自加了 1

次。

 

    public class VariantTest{

        public static int staticVar = 0; public int instanceVar = 0; public VariantTest(){

            staticVar++;

            instanceVar++;

            System.out.println(“staticVar=” + staticVar + ”,instanceVar=” + instanceVar);

        }

    }
} 


12、是否可以从一个 static 方法内部发出对非 static 方法的调用?

 

不可以。因为非 static 方法是要与对象关联在一起的,必须创建一个对象后,才可以在该对象上进行方法调用,而 static 方法调用时不需要创建对象,可以直接调用。也就是说,当一个 static 方法被调用时,


可能还没有创建任何实例对象,如果从一个 static 方法中发出对非 static 方法的调用,那个非 static 方法是关联到哪个对象上的呢?这个逻辑无法成立,所以,一个 static 方法内部发出对非 static 方法的调用。

 

13Integer  int 的区别

  

int  java 提供的 8 种原始数据类型之一。Java 为每个原始类型提供了封装类,Integer  java  int 提供的封装类。int 的默认值为 0,而 Integer 的默认值为 null,即 Integer 可以区分出未赋值和值为的区别,int 则无法表达出未赋值的情况,例如,要想表达出没有参加考试和考试成绩为的区别,则只能使用 Integer。在 JSP 开发中, Integer 的默认为 null,所以用 el 表达式在文本框中显示时,值为空白字符串,而 int 默认的默认值为 0,所以用 el 表达式在文本框中显示时,结果为 0,所以,int 不适合作为 web 层的表单数据的类型。在 Hibernate 中,如果将 OID 定义为 Integer 类型,那么 Hibernate

 

就可以根据其值是否为 null 而判断一个对象是否是临时的,如果将 OID 定义为了 int 类型,还需要在 hbm 映射文件中设置其 unsaved-value 属性为 0

 

另外,Integer 提供了多个与整数相关的操作方法,例如,将一个字符串转换成整数,Integer 中还定义了表示整数的最大值和最小值的常量。 

14  Math.round(11.5)  ?

 

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


Math 类中提供了三个与取整有关的方法:ceilfloorround,这些方法的作用与它们的英文名称的含义相对应,例如,ceil 的英文意义是天花板,该方法就表示向上取整,Math.ceil(11.3) 的结果为12,Math.ceil(-11.3)的结果是-11floor 的英文意义是地板,该方法就表示向下取整,Math.floor(11.6)的结果为 11,Math.floor(-11.6)的结果是-12;最难掌握的是 round 方法,它表示“四舍五入”,算法为

 Math.floor(x+0.5),即将原来的数字加上 0.5 后再向下取整,所以,Math.round(11.5)的结果为 12Math.round(-11.5)的结果为-11 

15 、请说出作用域 public  private protected,以及不写时的区别

 

这四个作用域的可见范围如下表所示。

说明:如果在修饰的元素上面没有写任何访问修饰符,则表示default

作用域当前类同一 package 子孙类其他 package

 

作用域

当前类 同一 package 子孙类 其他 package

public

protected

×

default

×

×

private

×

×

×

  

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

 

Overload 是重载的意思,Override 是覆盖的意思,也就是重写。重载 Overload 表示同一个类中可以有多个名称相同的方法,但这些方法的参数列表各不相同(即参数个数或类型不同)。

 

重写 Override 表示子类中的方法可以与父类中的某个方法的名称和参数完全相同,通过子类创建的实例对象调用这个方法时,将调用子类中的定义方法,这相当于把父类中定义的那个完全相同的方法给覆盖了,这也是面向对象编程的多态性的一种表现。子类覆盖父类的方法时,只能比父类抛出更少的异常,或者是抛出父类抛出的异常的子异常,因为子类可以解决父类的一些问题,不能比父类有更多的问题。子类方法的访问权限只能比父类的更大,不能更小。如果父类的方法是 private 类型,那么,子类则不存在覆盖的限制,相当于子类中增加了一个全新的方法。

至于Overloaded 的方法是否可以改变返回值的类型这个问题,要看你倒底想问什么呢?这个题目很模糊。如果几个 Overloaded 的方法的参数列表不一样,它们的返回者类型当然也可以不一样。但我估计你想问的问题是:如果两个方法的参数列表完全一样,是否可以让它们的返回值不同来实现重载 Overload。这是不行的,我们可以用反证法来说明这个问题,因为我们有时候调用一个方法时也可以不定义返回结果变量,即不要关心其返回结果,例如,我们调用map.remove(key)方法时,虽然 remove 方法有返回值,但是我们通常都不会定义接收返回结果的变量,这时候假设该类中有两个名称和参数列表完全相同的方法,仅仅是返回类型不同,java 就无法确定编程者倒底是想调用哪个方法了,因为它无法通过返回结果类型来判断。

 

override 可以翻译为覆盖,从字面就可以知道,它是覆盖了一个方法并且对其重写,以求达到不同的作用。对我们来说最熟悉的覆盖就是对接口方法的实现,在接口中一般只是对方法进行了声明,而我们在实现时,就需要实现接口声明的所有方法。除了这个典型的用法以外,我们在继承中也可能会在子类覆盖父类中的方法。在覆盖要注意以下的几点:

 

1、覆盖的方法的标志必须要和被覆盖的方法的标志完全匹配,才能达到覆盖的效果;2、覆盖的方法的返回值必须和被覆盖的方法的返回一致;3、覆盖的方法所抛出的异常必须和被覆盖方法的所抛出的异常一致,或者是其子类;4、被覆盖的方法不能为 private,否则在其子类中只是新定义了一个方法,并没有对其进行覆盖。

 overload 对我们来说可能比较熟悉,可以翻译为重载,它是指我们可以定义一些名称相同的方法,通过定义不同的输入参数来区分这些方法,然后再调用时,JVM 就会根据不同的参数样式,来选择合适的方法执行。在使用重载要注意以下的几点:

 

1、在使用重载时只能通过不同的参数样式。例如,不同的参数类型,不同的参数个数,不同的参数顺序(当然,同一方法内的几个参数类

 

型必须不一样,例如可以是 fun(int,float),但是不能为 fun(int,int));

 

2、不能通过访问权限、返回类型、抛出的异常进行重载;

 

3、方法的异常类型和数目不会对重载造成影响;


4、对于继承来说,如果某一方法在父类中是访问权限是 priavte,那么就不能在子类对其进行重载,如果定义的话,也只是定义了一个新方法,而不会达到重载的效果。

 

17 、构造器 Constructor 是否可被 override?

 

构造器 Constructor 不能被继承,因此不能重写 Override,但可以被

 

重载 Overload

 

18、接口是否可继承接口? 抽象类是否可实(implements)接口抽象类是否可继承具体类(concrete class)? 抽象类中是否可以有静态的 main 方法?

  

接口可以继承接口。抽象类可以实现(implements)接口,抽象类可继承具体类。抽象类中可以有静态的 main 方法。

 

备注:只要明白了接口和抽象类的本质和作用,这些问题都很好回答,你想想,如果你是 java 语言的设计者,你是否会提供这样的支持,如果不提供的话,有什么理由吗?如果你没有道理不提供,那答案就是肯定的了。

 

只有记住抽象类与普通类的唯一区别就是不能创建实例对象和允许有 abstract 方法。

 

19、写 clone()方法时,通常都有一行代码,是什么?

  

clone 有缺省行为,super.clone();因为首先要把父类中的成员复制到位,然后才是复制自己的成员。


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

  

计算机软件系统是现实生活中的业务在计算机中的映射,而现实生活中的业务其实就是一个个对象协作的过程。面向对象编程就是按现实业务一样的方式将程序代码按一个个对象进行组织和编写,让计算机系统能够识别和理解用对象方式组织和编写的程序代码,这样就可以把现实生活中的业务对象映射到计算机系统中。

 

面向对象的编程语言有封装、继承、抽象、多态等个主要的特征。 1 封装:

 

封装是保证软件部件具有优良的模块性的基础,封装的目标就是要实现软件部件的“高内聚、低耦合”,防止程序相互依赖性而带来的变动影响。在面向对象的编程语言中,对象是封装的最基本单位,面向对象的封装比传统语言的封装更为清晰、更为有力。面向对象的封装就是把描述一个对象的属性和行为的代码封装在一个“模块”中,也就是一个类中,属性用变量定义,行为用方法进行定义,方法可以直接访问同一个对象中的属性。通常情况下,只要记住让变量和访问这个变量的方法放在一起,将一个类中的成员变量全部定义成私有的,只有这个类自己的方法才可以访问到这些成员变量,这就基本上实现对象的封装,就很容易找出要分配到这个类上的方法了,就基本上算是会面向对象的编程了。把握一个原则:把对同一事物进行操作的方法和相关的方法放在同一个类中,把方法和它操作的数据放在同一个类中。

 

例如,人要在黑板上画圆,这一共涉及三个对象:人、黑板、圆,画圆的方法要分配给哪个对象呢?由于画圆需要使用到圆心和半径,圆心和半径显然是圆的属性,如果将它们在类中定义成了私有的成员变量,那么,画圆的方法必须分配给圆,它才能访问到圆心和半径这两个属性,人以后只是调用圆的画圆方法、表示给圆发给消息而已,画圆这个方法不应该分配在人这个对象上,这就是面向对象的封装性,即将对象封装成一个高度自治和相对封闭的个体,对象状态(属性)由这个对象自己的行为(方法)来读取和改变。一个更便于理解的例子就是,司机将火车刹住了,刹车的动作是分配给司机,还是分配给火车,显然,应该分配给火车,因为司机自身是不可能有那么大的力气将一个火车给停下来的,只有火车自己才能完成这一动作,火车需要调用内部的离合器和刹车片等多个器件协作才能完成刹车这个动作,司机刹车的过程只是给火车发了一个消息,通知火车要执行刹车动作而已。

 

抽象:

 

抽象就是找出一些事物的相似和共性之处,然后将这些事物归为一个类,这个类只考虑这些事物的相似和共性之处,并且会忽略与当前主题和目标无关的那些方面,将注意力集中在与当前目标有关的方面。例如,看到一只蚂蚁和大象,你能够想象出它们的相同之处,那就是抽象。抽象包括行为抽象和状态抽象两个方面。例如,定义一个

 

Person 类,如下: class Person{

 

String name; int age;

 

}

 

人本来是很复杂的事物,有很多方面,但因为当前系统只需要了解人的姓名和年龄,所以上面定义的类中只包含姓名和年龄这两个属性,这就是一种抽像,使用抽象可以避免考虑一些与目标无关的细节。我对抽象的理解就是不要用显微镜去看一个事物的所有方面,这样涉及的内容就太多了,而是要善于划分问题的边界,当前系统需要什么,就只考虑什么。

 

继承:

 

在定义和实现一个类的时候,可以在一个已经存在的类的基础之上来进行,把这个已经存在的类所定义的内容作为自己的内容,并可以加入若干新的内容,或修改原来的方法使之更适合特殊的需要,这就是继承。继承是子类自动共享父类数据和方法的机制,这是类之间的一种关系,提高了软件的可重用性和可扩展性。

 

多态:

 

多态是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。多态性增强了软件的灵活性和扩展性。例如,下面代码中的 UserDao 是一个接口,它定义引用变量 userDao 指向的实例对象由 daofactory.getDao() 在执行的时候返回,有时候指向的是 UserJdbcDao 这个实现,有时候指向的是 UserHibernateDao 这个实现,这样,不用修改源代码,就可以改变 userDao 指向的具体类实现,从而导致 userDao.insertUser()方法调用的具体代码也随之改变,即有时候调用的是 UserJdbcDao  insertUser 方法,有时候调用的是 UserHibernateDao  insertUser 方法:

 

UserDao userDao = daofactory.getDao(); userDao.insertUser(user);

 

比喻:人吃饭,你看到的是左手,还是右手?

 

 

21java 中实现多态的机制是什么?

 

 

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

 

22abstract class  interface 有什么区?

 

含有 abstract 修饰符的 class 即为抽象类,abstract 类不能创建的实例对象。含有 abstract 方法的类必须定义为 abstract classabstract class 类中的方法不必是抽象的。abstract class 类中定义抽象方法必须在具体(Concrete)子类中实现,所以,不能有抽象构造方法或抽象静态方法。如果的子类没有实现抽象父类中的所有抽象方法,那么子类也必须定义为 abstract 类型。

 

接口(interface)可以说成是抽象类的一种特例,接口中的所有方法都必须是抽象的。接口中的方法定义默认为 public abstract 类型,接口中的成员变量类型默认为 public static final

 

下面比较一下两者的语法区别:1.抽象类可以有构造方法,接口中不能有构造方法。2.抽象类中可以有普通成员变量,接口中是公开静态常量3.抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法。

 

4. 抽象类中的抽象方法的访问类型可以是 publicprotected 和(默认类型,虽然 eclipse 下不报错,但应该也不行),但接口中的抽象方法只能是 public

 

类型的,并且默认即为 public abstract 类型。

 

5. 抽象类中可以包含静态方法,接口中不能包含静态方法

 

6. 抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是 public static final 类型,并且默认即为 public static final 类型。

 

7. 一个类可以实现多个接口,但只能继承一个抽象类。下面接着再说说两者在应用上的区别:

 

接口更多的是在系统架构设计方法发挥作用,主要用于定义模块之间的通信契约。而抽象类在代码实现方面发挥作用,可以实现代码的重用,例如,模板方法设计模式是抽象类的一个典型应用,假设某个项目的所有 Servlet 类都要用相同的方式进行权限判断、记录访问日志和处理异常,那么就可以定义一个抽象的基类,让所有的 Servlet 都继承这个抽象基类,在抽象基类的 service 方法中完成权限判断、记录访问日志和处理异常的代码,在各个子类中只是完成各自的业务逻辑代码,伪代码如下: public abstract class BaseServlet extends HttpServlet{

 

public final void service(HttpServletRequest request, HttpServletResponse response) throws IOExcetion,ServletException {

记录访问日志进行权限判断

 

if(具有权限){ try{

 

doService(request,response);

 

}

 

catch(Excetpion e)  {

 

记录异常信息

 

}

 

}

 

}

 

protected abstract void doService(HttpServletRequest request, HttpServletResponse response) throws IOExcetion,ServletException; //注意访问权限定义成 protected,显得既专业,又严谨,因为它是专

门给子类用的

 

}

 

 

public class MyServlet1 extends BaseServlet

 

{

 

protected void doService(HttpServletRequest request, HttpServletResponse response) throws IOExcetion,ServletException

 

{

 

Servlet 只处理的具体业务逻辑代码

 

}

 

 

}

 

父类方法中间的某段代码不确定,留给子类干,就用模板方法设计模式。

 

23 abstract  method 是否可同时是static,是否可同时是 native,是否可同时是 synchronized?

 

abstract  method 不可以是 static ,因为抽象的方法是要被子类实现的,而 static 与子类扯不上关系! native 方法表示该方法要用另外一种依赖平台的编程语言实现的,不

 

存在着被子类实现的问题,所以,它也不能是抽象的,不能与 abstract混用。例如,FileOutputSteam 类要硬件打交道,底层的实现用的是操作系统相关的 api 实现,例如,在 windows 语言实现的,所以,查看 jdk 的源代码,可以发现 FileOutputStream  open 方法的定义如下: private native void open(String name) throws

FileNotFoundException;

 

如果我们要用 java 调用别人写的语言函数,我们是无法直接调用的,我们需要按照 java 的要求写一个语言的函数,又我们的这个语言函数去调用别人的语言函数。由于我们的语言函数是按 java 的要求来写的,我们这个语言函数就可以与 java 对接上,java 那边的对接方式就是定义出与我们这个函数相对应的方法,java 中对应的方法不需要写具体的代码,但需要在前面声明 native

 

关于 synchronized  abstract 合用的问题,我觉得也不行,因为在我几年的学习和开发中,从来没见到过这种情况,并且我觉得 synchronized 应该是作用在一个具体的方法上才有意义。而且,方法上的 synchronized 同步所使用的同步锁对象是 this,而抽象方法上无法确定 this 是什么。

 

24、什么是内部类?Static Nested Class Inner Class 的不同。

 

内部类就是在一个类的内部定义的类,内部类中不能定义静态成员(静态成员不是对象的特性,只是为了找一个容身之处,所以需要放到一个类中而已,这么一点小事,你还要把它放到类内部的一个类中,过分了啊!提供内部类,不是为让你干这种事情,无聊,不让你干。我想可能是既然静态成员类似语言的全局变量,而内部类通常是用于创建内部对象用的,所以,把“全局变量”放在内部类中就是毫无意义的事情,既然是毫无意义的事情,就应该被禁止),内部类可以直接访问外部类中的成员变量,内部类可以定义在外部类的方法外面,也可以定义在外部类的方法体中,如下所示:

class Outer {

 

int out_x = 0;

 

public void method() {


  

Inner1 inner1 = new Inner1(); class Inner2 // 在方法体内部定义的内部类

 

{

 

public Inner2() { out_x = 3;

 

}

 

}

 

Inner2 inner2 = new Inner2();

 

}

 

public class Inner1 // 在方法体外面定义的内部类

{

 

}

 

}

 

在方法体外面定义的内部类的访问类型可以是 public,protecte,默认的,private 种类型,这就好像类中定义的成员变量有种访问类型一样,它们决定这个内部类的定义对其他类是否可见;对于这种情况,我们也可以在外面创建内部类的实例对象,创建内部类的实例对象时,一定要先创建外部类的实例对象,然后用这个外部类的实例对象去创建内部类的实例对象,代码如下:

 

Outer outer = new Outer();

 

Outer.Inner1 inner1 = outer.new Innner1();

 

 

在方法内部定义的内部类前面不能有访问类型修饰符,就好像方法中定义的局部变量一样,但这种内部类的前面可以使用 final  abstract 修饰符。这种内部类对其他类是不可见的其他类无法引用这种内部类,但是这种内部类创建的实例对象可以传递给其他类访问。这种内部类必须是先定义,后使用,即内部类的定义代码必须出现在使用该类之前,这与方法中的局部变量必须先定义后使用的道理也是一样的。这种内部类可以访问方法体中的局部变量,但是,该局部变量前必须加 final 修饰符。

 

对于这些细节,只要在 eclipse 写代码试试,根据开发工具提示的各类错误信息就可以马上了解到。

 

 

在方法体内部还可以采用如下语法来创建一种匿名内部类,即定义某一接口或类的子类的同时,还创建了该子类的实例对象,无需为该子


类定义名称: class Outer {

 

public void start() {

 

new Thread(new Runnable() { public void run() {

 

}; }).start();

 

 

}

 

}

 

最后,在方法外部定义的内部类前面可以加上 static 关键字,从而成为 Static Nested Class,它不再具有内部类的特性,所有,从狭义上讲,它不是内部类。Static Nested Class 与普通类在运行时的行为和功能上没有什么区别,只是在编程引用时的语法上有一些差别,它可以定义成 publicprotected、默认的、private 等多种类型,而普通类只能定义成 public 和默认的这两种类型。在外面引用 Static Nested Class 类的名称为“外部类名.内部类名”。在外面不需要创建外部类的实例对象,就可以直接创建 Static Nested Class,例如,假设 Inner 是定义在 Outer 类中的 Static Nested Class,那么可以使用如下语句创建 Inner 类:

 

Outer.Inner inner = new Outer.Inner(); 由于 static Nested Class 不依赖于外部类的实例对象,所以,static

Nested Class 能访问外部类的非 static 成员变量。当在外部类中访问

 

Static Nested Class 时,可以直接使用 Static Nested Class 的名字,而不需要加上外部类的名字了,在 Static Nested Class 中也可以直接引用外部类的 static 的成员变量,不需要加上外部类的名字。

 

在静态方法中定义的内部类也是 Static Nested Class,这时候不能在类前面加 static 关键字,静态方法中的 Static Nested Class 与普通方法中的内部类的应用方式很相似,它除了可以直接访问外部类中的 static 的成员变量,还可以访问静态方法中的局部变量,但是,该局部变量前必须加 final 修饰符。

 

备注:首先根据你的印象说出你对内部类的总体方面的特点:例如,在两个地方可以定义,可以访问外部类的成员变量,不能定义静态成员,这是大的特点。然后再说一些细节方面的知识,例如,几种定义方式的语法区别,静态内部类,以及匿名内部类。



25、内部类可以引用它的包含类的成员吗?

 

有没有什么限制?

 

 

完全可以。如果不是静态内部类,那没有什么限制!

 

如果你把静态嵌套类当作内部类的一种特例,那在这种情况下不可以访问外部类的普通成员变量,而只能访问外部类中的静态成员,例如,下面的代码: class Outer {

 

static int x;//如果x不加static关键字就会出错 static class Inner {

 

void test() { System.out.println(x);

 

}

 

}

 

}

 

 

 

26Anonymous Inner Class (匿名内部类)

 

是否可以 extends(继承)其它类,是否可以 implements(实现)interface(接口)?

 

可以继承其他类或实现其他接口。不仅是可以,而是必须是某个类的子类或者某个接口的实现类!

 

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

 

基本数据类型包括 byteintcharlongfloatdoubleboolean short java.lang.String 类是 final 类型的,因此不可以继承这个类、不能修改这个类。为了提高效率节省空间,我们应该用 StringBuffer 

 

28String s = "Hello";s = s + " world!";

 

这两行代码执行后,原始的 String 对象中的内容到底变了没有?

 

没有。因为 String 被设计成不可变(immutable)类,所以它的所有对象都是不可变对象。在这段代码中,原先指向一个 String 对象,内容是 "Hello",然后我们对进行了+操作,那么所指向的那个对象是否发生了改变呢?答案是没有。这时,不指向原来那个对象了,而指向了另一个 String 对象,内容为"Hello world!",原来那个对象还存在于内存之中,只是这个引用变量不再指向它了。

 

通过上面的说明,我们很容易导出另一个结论,如果经常对字符串进行各种各样的修改,或者说,不可预见的修改,那么使用 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 类型属性都指向同一个对象。上面的结论还基于这样一个事实:对于字符串常量,如果内容相同, Java 认为它们代表同一个 String 对象。而用关键字 new 调用构造器,总是会创建一个新的对象,无论内容是否相同。

 

至于为什么要把 String 类设计成不可变类,是它的用途决定的。其实不只 String,很多 Java 标准类库中的类都是不可变的。在开发一个系统的时候,我们有时候也需要设计不可变类,来传递一组相关的值,这也是面向对象思想的体现。不可变类有一些优点,比如因为它的对象是只读的,所以多线程并发访问也不会有任何问题。当然也有一些缺点,比如每个不同的状态都要一个对象来代表,可能会造成性能上的问题。所以Java 标准类库还提供了一个可变版本,即 StringBuffer

 

29、是否可以继承 String ?

 

 

String 类是 final 类故不可以继承。

 

30String s = new String("xyz");创建了几个 String Object? 二者之间有什么区别?

 

两个或一个,”xyz”对应一个对象,这个对象放在字符串常量缓冲区,常量”xyz”不管出现多少遍,都是缓冲区中的那一个。New String 每写一遍,就创建一个新的对象,它依据那个常量”xyz”对象的内容来创建出一个新 String 对象。如果以前就用过’xyz’,这句代表就不会创建”xyz”自己了,直接从缓冲区拿。

 

31String  StringBuffer 的区别

 

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

 

接着要举一个具体的例子来说明,我们要把 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;

 

}

 

在讲两者区别时,应把循环的次数搞成 10000 ,然后用 endTime-beginTime 来比较两者执行的时间差异,最后还要讲讲

 

StringBuilder  StringBuffer 的区别。

 

 

String 覆盖了 equals 方法和 hashCode 方法,而 StringBuffer 没有覆盖 equals 方法和 hashCode 方法,所以,将 StringBuffer 对象存储进 Java 集合类中时会出现问题。

 

32、数组有没有 length()这个方法? String有没有 length()这个方法?数组没有 length()这个方法,有 length 的属性。String 有有 length()这个方法。


 

33、下面这条语句一共创建了多少个对象:

 

String s="a"+"b"+"c"+"d";

  

7 个对象

 

34try {}里有一个 return 语句,那么紧跟在这个 try 后的 finally {}里的 code 会不会被执行,什么时候被执行,在 return 前还是后?

 

也许你的答案是在 return 之前,但往更细地说,我的答案是在 return 中间执行,请看下面程序代码的运行结果: class Test1 {

 

/**

 

* @param args

 

* add by zxx ,Dec 9, 2008 */

 

public static void main(String[] args) { // TODO Auto-generated method stub System.out.println(new Test1().test());

}

 

static int test() { int x = 1;

 

try { return x;

 

} finally { ++x;

 

}


}

 

}

 

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

 

1

 

 

运行结果是 1,为什么呢?主函数调用子函数并得到结果的过程,好比主函数准备一个空罐子,当子函数要返回结果时,先把结果放在罐子里,然后再将程序逻辑返回到主函数。所谓返回,就是子函数说,我不运行了,你主函数继续运行吧,这没什么结果可言,结果是在说这话之前放进罐子里的。

 

35final, finally, finalize 的区别。

 

final 用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。 finally 是异常处理语句结构的一部分,表示总是执行。

 

finalize  Object 类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等。JVM 不保证此方法总被调用。

 

36、运行时异常与一般异常有何异同?

 

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

 

37error  exception 有什么区别?

  

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


 

37Java 中的异常处理机制的简单原理和应用。

 

异常是指 java 程序运行时(非编译)所发生的非正常情况或错误,与现实生活中的事件很相似,现实生活中的事件可以包含事件发生的时间、地点、人物、情节等信息,可以用一个对象来表示,Java 使用面向对象的方式来处理异常,它把程序中发生的每个异常也都分别封装到一个对象来表示的,该对象中包含有异常的信息。

 

Java 对异常进行了分类,不同类型的异常分别用不同的Java 类表示,所有异常的根类为 java.lang.ThrowableThrowable 下面又派生了两个子类:Error  ExceptionError 表示应用程序本身无法克服和恢复的一种严重问题,程序只有死的份了,例如,说内存溢出和线程死锁等系统问题。Exception 表示程序还能够克服和恢复的问题,其中又分为系统异常和普通异常,系统异常是软件本身缺陷所导致的问题,也就是软件开发人员考虑不周所导致的问题,软件使用者无法克服和恢复这种问题,但在这种问题下还可以让软件系统继续运行或者让 界( ArrayIndexOutOfBoundsException ), 常(NullPointerException)、类转换异常(ClassCastException);普通异常是运行环境的变化或异常所导致的问题,是用户能够克服的问题,例如,网络断线,硬盘空间不够,发生这样的异常后,程序不应该死掉。 java 为系统异常和普通异常提供了不同的解决方案,编译器强制普通

 

异常必须 try..catch 处理或用 throws 声明继续抛给上层调用方法处理,所以普通异常也称为 checked 异常,而系统异常可以处理也可以不处理,所以,编译器不强制用 try..catch 处理或用 throws 声明,所以系统异常也称为 unchecked 异常。

 

38 、请写出你最常见到的 5  runtime exception

  

所谓系统异常,就是…..,它们都是 RuntimeException 的子类,在 jdkdoc 中查 RuntimeException 类,就可以看到其所有的子类列表,也

 

就是看到了所有的系统异常。我比较有印象的系统异常有:

 

NullPointerException ArrayIndexOutOfBoundsException  

 

ClassCastException

 

40java 中有几种方法可以实现一个线程?用什么关键字修饰同步方法? stop() suspend()方法为何不推荐使用?

 

java5 以前,有如下两种:第一种:

 

new Thread(){}.start();这表示调用 Thread 子类对象的 run 方法,new Thread(){}表示一个 Thread 的匿名子类的实例对象,子类加上 run

 

方法后的代码如下: new Thread(){

public void run(){

 

 

}

 

}.start();

 

第二种:

 

new Thread(new Runnable(){}).start();这表示调用 Thread 对象接受的 Runnable 对象的 run 方法,new Runnable(){}表示一个 Runnable

 

的匿名子类的实例对象,runnable 的子类加上run 方法后的代码如下: new Thread(new Runnable(){

 

public void run(){

 

}

 

}

 

).start();

 

java5 开始,还有如下一些线程池创建多线程的方式:

 

ExecutorService pool = Executors.newFixedThreadPool(3) for(int i=0;i<10;i++)

 

{


pool.execute(new Runable(){public void run(){}});

 

}

 

Executors.newCachedThreadPool().execute(new Runable(){public void run(){}});

 

Executors.newSingleThreadExecutor().execute(new Runable(){public void run(){}}); 有两种实现方法,分别使用 new Thread() new Thread(runnable)

 

形式,第一种直接调用thread run 方法,所以,我们往往使用Thread

 

子类,即 new SubThread()。第二种调用 runnable  run 方法。

 

 

有两种实现方法,分别是继承 Thread 类与实现 Runnable 接口用 synchronized 关键字修饰同步方法反对使用 stop(),是因为它不安全。它会解除由线程获取的所有锁定,

 

而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。结果很难检查出真正的问题所在。suspend()方法容易发生死锁。调用 suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被"挂起"的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不应该使用 suspend(),而应在自己的 Thread 类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。

 

41sleep()  wait() 有什么区别?

 

 

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

 

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

 

 

如果数据将在线程间共享。例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就是共享数据,必须进行同步存取。

 

当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有效率。

 

 

 

43、多线程有几种实现方法?同步有几种实现方法?

 

多线程有两种实现方法,分别是继承 Thread 类与实现 Runnable  

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



44、启动一个线程是用 run()还是 start()?

 

 

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

 

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

 

分几种情况:

 

1.其他方法前是否加了 synchronized 关键字,如果没加,则能。 2. 如果这个方法内部调用了 wait ,则可以进入其他

synchronized 方法。

 

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

4.如果其他方法是 static,它用的同步锁是当前类的字节码,与非静态的方法不能同步,因为非静态的方法用的是 this

 

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

 

一个程序中可以有多条执行线索同时执行,一个线程就是程序中的一条执行线索,每个线程上都关联有要执行的代码,即可以有多段程序代码同时运行,每个程序至少都有一个线程,即 main 方法执行的那个线程。如果只是一个 cpu,它怎么能够同时执行多段程序呢?这是从宏观上来看的,cpu 一会执行线索,一会执行线索,切换时间很快,给人的感觉是 a,b 在同时执行,好比大家在同一个办公室上网,只有一条链接到外部网线,其实,这条网线一会为传数据,一会为 b 传数据,由于切换时间很短暂,所以,大家感觉都在同时上网。

状态:就绪,运行,synchronize 阻塞,wait  sleep 挂起,结束。 wait 必须在 synchronized 内部调用。

 

调用线程的 start 方法后线程进入就绪状态,线程调度系统将就绪状态的线程转为运行状态,遇到 synchronized 语句时,由运行状态转为阻塞,当 synchronized 获得锁后,由阻塞转为运行,在这种情况可以调用 wait 方法转为挂起状态,当线程关联的代码执行完后,线程变为结束状态。

 

 

 

47、简述 synchronized  java.util.con

 

 

current.locks.Lock 的异同?

 

 

主要相同点:Lock 能完成 synchronized 所实现的所有功能

 

主要不同点:Lock 有比 synchronized 更精确的线程语义和更好的性能。synchronized 会自动释放锁,而 Lock 一定要求程序员手工释放,并且必须在 finally 从句中释放。Lock 还有更强大的功能,例如,它的 tryLock 方法可以非阻塞方式去拿锁。

 

48Collection 框架中实现比较要实现什么

 

接口

 

 

comparable/comparator

 

 

49ArrayList  Vector 的区别

 

 

答:

 

这两个类都实现了 List 接口(List 接口继承了 Collection 接口),他们都是有序集合,即存储在这两个集合中的元素的位置都是有顺序的,相当于一种动态的数组,我们以后可以按位置索引号取出某个元素,,并且其中的数据是允许重复的,这是 HashSet 之类的集合的最大不同处,HashSet 之类的集合不可以按索引号去检索其中的元素,也不允许有重复的元素(本来题目问的与 hashset 没有任何关系,但为了说清楚 ArrayList  Vector 的功能,我们使用对比方式,更有利于说明问题)。

 

 

接着才说 ArrayList  Vector 的区别,这主要包括两个方面:.1)同步性:

 

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

 

备注:对于 Vector&ArrayListHashtable&HashMap,要记住线程安全的问题,记住 Vector  Hashtable 是旧的,是 java 一诞生就提供了的,它们是线程安全的,ArrayList  HashMap  java2 时才提供的,它们是线程不安全的。所以,我们讲课时先讲老的。

 

2)数据增长:

 

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

 

总结:即 Vector 增长原来的一倍,ArrayList 增加原来的 0.5 倍。

 

50HashMap  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

 

51List  Map 区别?

 

 

一个是存储单列数据的集合,另一个是存储键和值这样的双列数据的集合,List 中存储的数据是有顺序,并且允许重复;Map 中存储的数据是没有顺序的,其键是不能重复的,它的值是可以有重复的。

 

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

 

ListSet 是,Map 不是


 

53ListMapSet 三个接口,存取元素时,各有什么特点?

 

首先,List  Set 具有相似性,它们都是单列元素的集合,所以,它们有一个功共同的父接口,叫 CollectionSet 里面不允许有重复的元素,所谓重复,即不能有两个相等(注意,不是仅仅是相同)的对象,即假设 Set 集合中有了一个对象,现在我要向 Set 集合再存入一个对象,但对象与对象 equals 相等,则对象存储不进去,所以,Set 集合的 add 方法有一个 boolean 的返回值,当集合中没有某个元素,此时 add 方法可成功加入该元素时,则返回 true,当集合含有与某个元素 equals 相等的元素时,此时 add 方法无法加入该元素,返回结果为 falseSet 取元素时,没法说取第几个,只能以 Iterator 接口取得所有的元素,再逐一遍历各个元素。

 

List 表示有先后顺序的集合,注意,不是那种按年龄、按大小、按价格之类的排序。当我们多次调用 add(Obj e)方法时,每次加入的对象就像火车站买票有排队顺序一样,按先来后到的顺序排序。有时候,也可以插队,即调用 add(int index,Obj e)方法,就可以指定当前对象在集合中的存放位置。一个对象可以被反复存储进 List 中,每调用一次 add 方法,这个对象就被插入进集合中一次,其实,并不是把这个对象本身存储进了集合中,而是在集合中用一个索引变量指向这个对象,当这个对象被 add 多次时,即相当于集合中有多个索引指向了这个对象,如图所示。List 除了可以以 Iterator 接口取得所有的元素,再逐一遍历各个元素之外,还可以调用 get(index i)来明确说明取第几个。

 

Map  List  Set 不同,它是双列的集合,其中有 put 方法,定义如下:put(obj key,obj value),每次存储时,要存储一对 key/value

 

不能存储重复的 key,这个重复的规则也是按 equals 比较相等。取则可以根据 key 获得相应的 value,即 get(Object key)返回值为 key

 

所对应的 value。另外,也可以获得所有的 key 的结合,还可以获得所有的 value 的结合,还可以获得 key  value 组合成的 Map.Entry

对象的集合。

 

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 中却加不了多次的。

 

54、说出 ArrayList,Vector, LinkedList 存储性能和特性


ArrayList  Vector 都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢,Vector 由于使用了 synchronized 方法(线程安全),通常性能上较 ArrayList 差,而 LinkedList 使用双向链表实现存储,按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。

 

LinkedList 也是线程不安全的,LinkedList 提供了一些方法,使得

 

LinkedList 可以被当作堆栈和队列来使用。

 

55、去掉一个 Vector 集合中重复的元素

 

Vector newVector = new Vector();

 

//添加元素之前,先检测Vector中是否存在过这个元素,如果不存在,再添加该元素

 

for (int i = 0; i < newVector.size(); i++) {


 Object obj = newVector.get(i); if (!newVector.contains(obj))

 

newVector.add(obj);

 

}

 

还有一种简单的方式,HashSet set = new HashSet(vector);

 

56Collection  Collections 的区别。

  

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

 

57Set 里的元素是不能重复的,那么用什么方法来区分重复与否呢是用==还是equals()? 它们有何区别?

  

Set 里的元素是不能重复的,元素重复与否是使用 equals()方法进行判断的。equals()==方法决定引用值是否指向同一对象 equals()在类中被覆

盖,为的是当两个分离的对象的内容和类型相配的话,返回真值。

 

 

58、你所知道的集合类都有哪些?主要方法?

 

最常用的集合类是 List  Map List 的具体实现包括 ArrayList

 

Vector,它们是可变大小的列表,比较适合构建、存储和操作任何类型对象的元素列表。 List 适用于按数值索引访问元素的情形。 Map 提供了一个更通用的元素存储方法。 Map 集合类用于存储元素对(称作""""),其中每个键映射到一个值。

 

ArrayList/VectoràList àCollection


HashSet/TreeSetàSet

 

PropetiesàHashTable

 

àMap

 

Treemap/HashMap

 

我记的不是方法名,而是思想,我知道它们都有增删改查的方法,但这些方法的具体名称,我记得不是很清楚,对于 set,大概的方法是 add,remove, contains;对于 map,大概的方法就是 put,remove contains 等,因为,我只要在 eclispe 下按点操作符,很自然的这些方法就出来了。我记住的一些思想就是 List 类会有 get(int index)这样的方法,因为它可以按顺序取元素,而 set 类中没有 get(int index) 这样的方法。List  set 都可以迭代出所有元素,迭代时先要得到一个 iterator 对象,所以,set  list 类都有一个 iterator 方法,用于返回那个 iterator 对象。map 可以返回三个集合,一个是返回所有的 key 的集合,另外一个返回的是所有 value 的集合,再一个返回的 key  value 组合成的 EntrySet 对象的集合,map 也有 get 方法,参数是key,返回值是 key 对应的 value

 

 

 

59、两个对象值相同(x.equals(y) == true)

 

但却可有不同的 hash code,这句话对不对?

 

对。

 

如果对象要保存在 HashSet  HashMap 中,它们的 equals 相等,那么,它们的 hashcode 值就必须相等。

 

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


60java 中有几种类型的流?JDK 为每种

 

类型的流提供了一些抽象类以供继承,请说出他们分别是哪些类?

 

字节流,字符流。字节流继承于 InputStream OutputStream,字符流继承于 InputStreamReader OutputStreamWriter。在 java.io 包中还有许多其他的流,主要是为了提高性能和使用方便。

 

61、字节流与字符流的区别

 

要把一片二进制数据数据逐一输出到某个设备中,或者从某个设备中逐一读取一片二进制数据,不管输入输出设备是什么,我们要用统一的方式来完成这些操作,用一种抽象的方式进行描述,这个抽象描述方式起名为 IO 流,对应的抽象类为 OutputStream  InputStream 

 

不同的实现类就代表不同的输入和输出设备,它们都是针对字节进行操作的。

 

在应用中,经常要完全是字符的一段文本输出去或读进来,用字节流可以吗?计算机中的一切最终都是二进制的字节形式存在。对于“中国”这些字符,首先要得到其对应的字节,然后将字节写入到输出流。读取时,首先读到的是字节,可是我们要把它显示为字符,我们需要将字节转换成字符。由于这样的需求很广泛,人家专门提供了字符流的包装类。

 

底层设备永远只接受字节数据,有时候要写字符串到底层设备,需要将字符串转成字节再进行写入。字符流是字节流的包装,字符流则是直接接受字符串,它内部将串转成字节,再写入底层设备,这为我们向 IO 设别写入或读取字符串提供了一点点方便。

 

字符向字节转换时,要注意编码的问题,因为字符串转成字节数组,其实是转成该字符的某种编码的字节形式,读取也是反之的道理。


62、什么是 java 序列化,如何实现 java 序列化?或者请解释 Serializable 接口的作用。

 

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

 

63、描述一下 JVM 加载 class 文件的原理机制?

 

JVM 中类的装载是由 ClassLoader 和它的子类来实现的,Java ClassLoader 是一个重要的 Java 运行时系统组件。它负责在运行时查找和装入类文件的类。

 

64heap  stack 有什么区别。

  

java 的内存分为两类,一类是栈内存,一类是堆内存。栈内存是指程序进入一个方法时,会为这个方法单独分配一块私属存储空间,用于存储这个方法内部的局部变量,当这个方法结束时,分配给这个方法的栈会释放,这个栈中的变量也将随之释放。

 

堆是与栈作用不同的内存,一般用于存放不放在当前方法栈中的那些数据,例如,使用 new 创建的对象都放在堆里,所以,它不会随方法的结束而消失。方法中的局部变量使用 final 修饰后,放在堆中,而不是栈中。

 

65GC 是什么? 为什么要有 GC?

GC 是垃圾收集的意思(Gabage Collection,内存处理是编程人员

 

容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java 提供的 GC 功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,Java 语言没有提供释放已分配内存的显示操作方法。

 

66、垃圾回收的优点和原理。并考虑 2 种回收机制。

 

Java 语言中一个显著的特点就是引入了垃圾回收机制,使 c++程序员最头疼的内存管理的问题迎刃而解,它使得 Java 程序员在编写程序的时候不再需要考虑内存管理。由于有个垃圾回收机制,Java 中的对象不再有"作用域"的概念,只有对象的引用才有"作用域"。垃圾回收可以有效的防止内存泄露,有效的使用可以使用的内存。垃圾回收器通常是作为一个单独的低级别的线程运行,不可预知的情况下对内存堆中已经死亡的或者长时间没有使用的对象进行清楚和回收,程序员不能实时的调用垃圾回收器对某个对象或所有对象进行垃圾回收。回收机制有分代复制垃圾回收和标记垃圾回收,增量垃圾回收。


67、垃圾回收器的基本原理是什么?垃圾回收器可以马上回收内存吗?有什么办法主动通知虚拟机进行垃圾回收?

 

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

 

68java 中会存在内存泄漏吗,请简单描述。

 

 

所谓内存泄露就是指一个不再被程序使用的对象或变量一直被占据在内存中。java 中有垃圾回收机制,它可以保证一对象不再被引用的时候,即对象编程了孤儿的时候,对象将自动被垃圾回收器从内存中清除掉。由于 Java 使用有向图的方式进行垃圾回收管理,可以消除引用循环的问题,例如有两个对象,相互引用,只要它们和根进程不可达的,那么 GC 也是可以回收它们的 java 中的内存泄露的情况:长生命周期的对象持有短生命周期对象的

 

引用就很可能发生内存泄露,尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收,这就是 java 中内存泄露的发生场景,通俗地说,就是程序员可能创建了一个对象,以后一直不再使用这个对象,这个对象却一直被引用,即这个对象无用但是却无法被垃圾回收器回收的,这就是 java 中可能出现内存泄露的情况,例如,缓存系统,我们加载了一个对象放在缓存中(例如放在一个全局 map 对象中),然后一直不再使用它,这个对象一直被缓存引用,但却不再被使用。

 

检查 java 中的内存泄露,一定要让程序将各种分支情况都完整执行到程序结束,然后看某个对象是否被使用过,如果没有,则才能判定这个对象属于内存泄露。

如果一个外部类的实例对象的方法返回了一个内部类的实例对象,这个内部类对象被长期引用了,即使那个外部类实例对象不再被使用,但由于内部类持久外部类的实例对象,这个外部类对象将不会被垃圾回收,这也会造成内存泄露。

 

69 、能不能自己写个类,也叫java.lang.String

 

可以,但在应用的时候,需要用自己的类加载器去加载,否则,系统的类加载器永远只是去加载 jre.jar 包中的那个 java.lang.String。由于在 tomcat  web 应用程序中,都是由 webapp 自己的类加载器先自己加载 WEB-INF/classess 目录中的类,然后才委托上级的类加载器加载,如果我们在tomcat web 应用程序中写一个java.lang.String,这时候 Servlet 程序加载的就是我们自己写的java.lang.String,但是这么干就会出很多潜在的问题,原来所有用了 java.lang.String 类的都将出现问题。 

虽然 java 提供了 endorsed 技术,可以覆盖 jdk 中的某些类,具体做法是….。但是,能够被覆盖的类是有限制范围,反正不包括 java.lang 这样的包中的类。


猜你喜欢

转载自blog.csdn.net/qq_40992849/article/details/80981254