很久没有复习java基础,感觉应该给自己记录一下看过的题目,以便以后可以随时翻出来看看,方便学习。

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

答:

        面向对象的特征主要有以下几个方面:

抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,不关注这些行为的细节是什么。

继承:继承是从已有类得到继承信息创建新类的过程。提供继承信息的类被称为父类(超累、基类);得到继承信息的类被称为子类(派生类)。继承让变化中的软件系统有了一定的延续性,同时继承也是封装程序中可变因素的重要手段。

封装:通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。面向对象的本质就是将现实世界描绘成一系列完全自治、封闭的对象。我们在类中编写的方法就是对现实细节的一种封装;我们编写一个类就是对数据和数据操作的封装。可以说,封装就是隐藏一切可隐藏的东西,只向外界提供最简单的编程接口(可以想象普通洗衣机和全自动洗衣机的差别,全自动洗衣机封装更好,所以操作起来更简单;我们使用的智能手机也是封装得够好,所以几个按键就搞定了所有事情)。

多态性:多态性是指允许不同子类型的对象对同一消息作出不同的响应。简单的说,就是用同样的对象引用调用同样的方法,但是做了不同的事情。多态性分为编译时的多态性和运行时的多态性。如果将对象的方法视为对象向外界提供的服务,那么运行时的多态性可以解释为:当系统A访问系统B提供的服务时,系统B有多种提供服务的方式,但一切对系统A来说都是透明的(举个例子:电动理发推子是系统A,它的供电系统是系统B,系统B可以使用电池或交流电供电,还可以用太阳能供电,系统A只通过系统B调用供电的方法,不用知道供电系统底层的实现是什么,究竟是通过什么方式获得的电力)。方法重载(overload)实现的是编译时的多态性(也称为前绑定),而方法重写(override)实现的是运行时的多态性(也称为后绑定)。运行时的多态是面向对象最精髓的东西,要实现多态需要做两件事:①.方法重写(子类继承父类并重写父类中已有的或抽象的方法);②.对象造型(用父类型引用子类型对象,这样同样的引用调用同样的方法,就会根据子类对象的不同而表现出不同的行为)。

2、访问修饰符public,private,protected以及不写(默认)时的区别?

答:

修饰符

当前类

同 包

子 类

其他包

public





protected




×

default



×

×

private


×

×

×

类的成员不写访问修饰符时,默认为default。默认对于同一个包中的其他类相当于public(公开),对于不是同一个包中的其他类相当于private(私有)。protected(受保护)对于子类相当于public(公开),对不是同一包中没有父子关系的类相当于私有。外部类的修饰符只能说public或者默认,类的成员(包括内部类)的修饰符可以是以上四种。

3、float f = 3.4; 是否正确?

答:不正确。3.4是双精度型(double),将double赋值给浮点型(float)属于下转型(down-casting,也称为窄化),会造成精度损失,因此需要强制类型转换 float f = (float)3.4; 或者写成 float f = 3.4F; 。

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

答:String不是最基本的数据类型。

        Java中的基本数据类型有8种: byte、short、int、long、float、double、char、boolean;除了基本类型(primitive type),剩下的都是引用类型(reference type),Java5 以后引入的枚举类型也算是一种比较特殊的引用类型。

5、 short s1 = 1; s1 = s1 + 1;         有错吗?

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

答:

short s1 = 1; s1 = s1 + 1;        编译报错。因为1是int类型,因此 s1 + 1 运算结果也是int型,需要强制转换类型才能赋值给short型

short s1 = 1;s1 += 1;       正确编译。因为s1 += 1;相当于s1 = (short)(s1 + 1);其中有隐含的强制类型转换。

6、int和Integer的区别?

答:int是基本类型,Integer是引用类型。Java的每一个基本类型都引入了对应的包装类型(wrapper class),int的包装类就是Integer,从Java5开始引入了  自动装箱/拆箱机制,使得二者可以相互转换。

                 基本类型:byte, short,   char,           int,         long,  float, double, boolean        

包装类型(引用类型):Byte,  Short,  Character, Integer, Long, Float, Double, Boolean

举例子 1:

class Test1{
        public static void main(String[] args) {
                Integer a = new Integer(4);
                Integer b = 4;        //将4自动装箱成Integer类型
                int c = 4;
                System.out.println(a == b);         //false 两个引用没有引用同一对象
                System.out.println(a == c);         // true a自动拆箱成int类型,再和c比较
        }
}

举例子2:

public class Test2{
        public static void main(String[] args){
                Integer i1 = 100, i2 = 100, i3 = 150, i4 = 150;
                System.out.println(i1 == i2);
                System.out.println(i3 == i4);
        }
}

如果不注意,就很容易会认为两个输出要么都是true,要么都是false。首先要注意的是i1、i2、i3、i4四个变量都是Integer对象,所以==运算比较的不是值,而是引用。
简单的说,(1)如果整型变量的值在-128~127之间的话,它们被装箱为Integer对象后,会存在内存中被重用,始终只存在一个对象。
(2)而如果在-128~127之外的数,被装箱后的Integer对象并不会被重用,即相当于每次装箱时都新建一个 Integer对象。

所以:

System.out.println(i1 == i2);        // true
System.out.println(i3 == i4);        //false

7、&和&&的区别?

答:&运算符有两种用法:①按位与;②逻辑与。

        &&运算符是短路与运算。

        逻辑与跟短路与的差别是非常巨大的,虽然二者都要求运算符左右两端的布尔值都是true,整个表达式的值才是true。

注意:逻辑或运算符 | 和短路或运算符  || 的差别也是如此。

补充:JavaScript中更能感受到短路运算符的强大。

8、Java有没有goto?

答:goto是Java中的保留关键字,在目前的Java版本里没有使用它。

Java之父James Gosling编写的《The Java Programming Language》一书的附录中给出了一个Java关键字列表,其中有goto和const,但是这两个是目前无法使用的关键字,因此有些地方将其称之为保留字,其实保留字这个词应该有更广泛的意义,因为熟悉C语音的程序员都知道,在系统类库中使用过的有特殊意义的单词或单词的组合都被视为保留字。

goto虽然在Java中作为保留字,但是并没有去实现它。

但是却可以通过Java的  标签 (Label)来实现goto的类似效果(Java中的标签是为循环设计的,是为了在多重循环中方便的使用 break和continue。),看看以下实例:

代码:

import java.io.*;
 import java.util.Arrays;public class test{
     public static void main(String[] args){
         // System.out.println(new Square().area());
          //此处可以自定义标签名
         outer:                // 最外层循环的标签
         for (int i = 0; i < 10; i++) {
           inter:                //内存循环的标签
           for (int j = 0; j < 10; j++) {
                 if (i <= 2 && j == 4){                
                   System.out.println("跳出内层循环:"+i);
                   break inter;
                 }
                 if (j == 5) {
                   System.out.println("跳出最外层循环:"+i);
                   break outer;
                 }
                 System.out.println("j = "+j);
           }
         }
     }    
 }

运行结果:

Java面试题怎么理解面向对象 java面向对象试题及答案_java

 发现可以随心所欲的跳出各处循环。

 再看看反编译的结果:

Java面试题怎么理解面向对象 java面向对象试题及答案_Java面试题怎么理解面向对象_02

 可以看出,其实Java字节码在底层是使用了goto的。

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

答:在最外层循环前加一个标签标记如Temp,然后用break Temp; 可以跳出多重循环。

(Java中支持带标签的break和continue语句,作用有点类似于C和C++的goto语句,但是就像要避免使用goto一样,应该避免使用带标签的break和continue,因为它不会让你的程序变得更优雅,很多时候可能会有相反的作用,所以这种语法不知道也没关系。)

10、解释内存中的栈(stack)、堆(heap)和方法区(method area)的用法?

答:通常定义的一个基本数据类型的变量,一个对象的引用,或者函数调用的现场保存都是用JVM的栈(stack)空间;而通过new关键字和构造器创建的对象则放在堆(heap)空间堆是垃圾收集器管理的主要区域,由于现在的垃圾收集器都采用分代收集算法,所以堆空间还可以细分为新生代和老生代,再具体一点,可以分为Eden、Survivor(又可分为From Survivor和To Survivor)、Tenured;方法区和堆都是各个线程共享的内存区域,用于存储已经被JVM加载的类信息、常量、静态变量、JIT编译器编译后的代码等数据;程序中的字面量(literal)如直接书写的100、”hello world“和常量都是放在常量池中,常量池是方法区的一部分。栈空间操作起来最快但栈很小,通常大量的对象都是放在堆空间,栈和堆的大小都可以通过JVM的启动参数来进行调整,栈空间用光了会引发StackOverflowError,而堆和常量池空间不足则会引发OutOfMemeryError。

举例:

String str = new String("hello");

上面的语句中变量str放在栈上,用new创建出来的字符串对象放在堆上,而”hello“这个字面量是放在方法区的。

补充1:较新版本的Java(从Java6的某个更新开始)中,由于JIT编译器的发展和”逃逸分析“技术的逐渐成熟,栈上分配、标量替换等优化技术使得对象一定分配在堆上这件事情已经变得不那么绝对了。

补充2:运行时常量池相当于Class文件常量池具有动态性,Java语音并不要求常量一定只有编译期间才能产生,运行期间也可以将新的常量放入池中,String类的intern()方法就是这样的。

看看下方代码的执行结果是什么,并比较以下Java7以前和以后的运行结果是否一致:

String s1 = new StringBuilder("go").append("od").toString();
System.out.println(s1.intern() == s1);
String s2 = new StringBuilder("ja").append("va").toString();
System.out.println(s2.intern() == s2);
运行结果:true        false

JDK1.7(以及部分其他虚拟机,如JRockit)的intern()实现不会再复制实例,只是在常量池中记录首次出现的实例引用,因此intern()返回的引用和由StringBuilder创建的那个字符串实例是同一个。

对s2返回false,是因为”java“这个字符串在执行StringBuilder.toString()之前已经出现过,字符串常量池中已经有它的引用了,不符合”首次出现“的原则,所以返回false;而"good"这个字符串则是首次出现的,所以返回true。

11、switch是否能作用在byte上,是否能作用在long上,是否能作用在String上?

答:在switch(expr)中,在Java5以前,expr只能说byte、short、char、int。

从Java5开始,Java中引入了枚举类型,expr也可以是枚举类型(enum);

从Java7开始,expr还可以是字符串(String)。

但是在目前所有的版本中,switch都是不可以作用在长整型(long)上的。

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

答:Math.round(11.5)的返回值是12,Math.round(-11.5)的返回值是-11。

四舍五入的原理是在参数上加0.5然后进行向下取整。

13、用最有效率的方法计算2乘以8?

答:2<<3(左移3位相当于乘以2的3次方,右移3位相当于除以2的3次方)。

14、数组有没有length()方法?String有没有length()方法?

答:数组没有length()方法,有length属性。String有length()方法。

JavaScript中,获得字符串长度是通过length属性得到的,这点容易和Java混淆。

15、构造器(constructor)是否可被重写(override)?

答:构造器不能被继承,因此不能被重写。但是可以被重载(overload)。

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

答:不对,如果两个对象x和y满足x.equals(y) == true,它们的哈希码(hash code)应当相同。

Java对于equals方法和hashCode方法是这样规定的:(1)如果两个对象相同(equals方法返回true),那么它们的hashCode值一定要相同;(2)如果两个对象的hashCode相同,它们并不一定相同。当然,你未必要按照要求去做,但是如果你违背了上述原则就会发现在使用容器时,相同的对象可以出现在Set集合中,同时增加新元素的概率会大大下降(对于使用哈希存储的系统,如果哈希码频繁的冲突将会造成存取性能急剧下降)。

对于equals方法,首先equals方法必须满足自反性(x.equals(x)必须返回true)、对称性(x.equals(y)返回true时,y.equals(x)也必须返回true)、传递性(x.equals(y)和y.equals(z)都返回true时,x.equals(z)也必须返回true)和一致性(当x和y引用的对象信息没有被修改时,多次调用x.equals(y)应该得到同样的返回值),而且对于任何非null值的引用x,x.equals(null)必须返回false。实现高质量的equals方法的诀窍包括:

1.使用==操作符检查"参数是否为这个对象的引用";

2.使用instanceof操作符检查"参数是否为正确的类型";

3.对于类中的关键属性,检查参数传入对象的属性是否与之相匹配;

4.编写完equals方法后,问自己它是否满足对称性、传递性、一致性;

5.重写equals时总是要重写hashCode;

6.不要将equals方法参数中的Object对象替换为其他的类型,在重写时不要忘掉@Override注解。

17、是否可以继承String类?

答:String类是final类,不可以被继承。

继承String类本身就是一个错误的行为,对String类型最好的重用方式是关联关系(Has-A)和依赖关系(Use-A),而不是继承关系(Is-A)。

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

答:是值传递。

Java语音的方法调用只支持参数的值传递。当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的属性可以在被调用过程中被改变,但对对象引用的改变是不会影响到调用者的。C++和C#中可以通过传引用或传输出参数来改变传入的参数的值。在C#中可以编写如下代码,但是在Java中却做不到。

C#代码实例:

using System;
namespace TEST{
        class Program {
                public static void swap(ref int x, ref int y) {
                        int temp = x;
                        x = y;
                        y = temp;
                }
                public static void Main(string[] args){
                        int a = 5, b = 10;
                        swap(ref a, ref b);
                        // a = 10, b = 5;
                        Console.WriteLine("a = {0}, b = {1}", a, b);
                }
        }
}

说明:Java中没有传引用实在是非常的不方便,这一点在Java8中仍然没有得到改进,正是如此在Java编写的代码中才会出现大量的包装类(Wrapper:8个基本类型有对应的8个包装类)(将需要通过方法调用修改的引用置于一个Wrapper类中,再将Wrapper对象传入方法),这样的做法只会让代码变得臃肿。

19、重载(Overload)和重写(Override)的区别?重载的方法能否根据返回类型进行区分?

答:方法的重载和重写都是实现多态的方式,区别在于:

前者实现的是编译时的多态性,而后者实现的是运行时的多态性。

1.重写必须依赖于继承(父子关系) 将父类的的方法给完善。而重载不需要依赖继承关系,在自己的类中就可以实现。
2.重写的方法名必须和父类的方法名相同,如果父类没有这个方法,那么就不算重写。重载是在自身类中创建多个方法名相同但是类型不同的方法。
3.重写的参数不能修改,而重载的参数必须修改。

重载的条件

1.在同一个类中。
2.方法名相同。
3.参数列表中的参数类型、个数、顺序不同。

重载的作用

1.可以根据不同的参数类型调用不同的重载方法,在调用时候更加方便快捷。
2.使代码更加灵活。

重写概念

1.重写必须依赖于继承,将父类的方法完善。
2.重写的方法要和父类的方法名一致。
3.重写方法的返回值和参数不能改变。

重写的好处

子类可以完善父类之中不足之处,使自身的方法更加的完善。

衍生的一个面试题:"为什么不能根据返回类型来区分重载?"

答:通过以上重载的条件可知,方法的重载要求"两同"、"三不同"

1. 同一个类中

2. 方法名相同

3. 参数类型、个数、顺序不同

如果根据返回类型区分重载,假设只调用方法,不获取返回值的时候,由于重载是在同一个类中,此时JVM就不知道需要调用哪一个返回类型的方法了。

20、String和StringBuilder、StringBuffer的区别?

答:Java平台提供了两种类型的字符串:String和StringBuffer/StringBuilder,它们可以储存和操作字符串。其中String是只读字符串,也就意味着String引用的字符串内容是不能被改变的。而StringBuffer/StringBuilder类表示的字符串对象可以直接进行修改。

StringBuilder是Java5中引入的,它和StringBuffer的方法完全相同,区别在于它是在单线程环境下使用的,因为它的所有方面都没有被synchronized修饰,因此它的效率比StringBuffer要高。

补充面试题1:什么情况下用+运算符进行字符串连接比调用StringBuffer/StringBuilder对象的append方法连接字符串性能更好?

答:对于字面量字符的连接,String+的性能更好,如 String s5="aaa"+"bb"+"cc";其实就是String s5="aaabbcc";此时常量池中有“aaabbcc”;而对于大部分情况,StringBuilder性能优于String。

补充面试题2:请说出下面程序的输出。

import java.io.*;
import java.util.Arrays;public class test{    
     public static void main(String[] args){
         // System.out.println(new Square().area());
         String s1 = "Programming";
         String s2 = new String("Programming");
         String s3 = "Program";
         String s4 = "ming";
         String s5 = "Program" + "ming";
         String s6 = s3 + s4;
         
         System.out.println(s1 == s2); // false
         System.out.println(s1 == s5); // true
         System.out.println(s1 == s6); //false
         System.out.println(s1 == s6.intern());    //true
         System.out.println(s2 == s2.intern());    //false
     }         
 }

注意:

1.String对象的intern方法会得到字符串对象在常量池中对应的版本的引用(如果常量池中有一个字符串与String对象的equals结果是true),如果常量池中没有对应的字符串,则该字符串将被添加到常量池中,然后返回常量池中字符串的引用;

2.字符串的+操作其本质是创建了StringBuilder对象进行append操作,然后将拼接后的StringBuilder对象用toString()方法处理成String对象。

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

答:JVM中类的装载是由类加载器(ClassLoader)和它的子类来实现的,Java中的类加载器是一个重要的Java运行时系统组件,它负责在运行时查找和装入类文件中的类。由于Java的跨平台性,经过编译的Java源程序并不是一个可执行程序,而是一个或多个类文件。当Java程序需要使用某个类时,JVM会确保这个类已经被加载、连接(验证、准备和解析)和初始化。类的加载是指把类的.class文件中的数据读入到内存中,通常是创建一个字节数组读入.class文件,然后产生与所加载类对应的Class对象。加载完成后,Class对象还不完整,所以此时的类还不可用。当类被加载后就进入连接阶段,这一阶段包括验证、准备(为静态变量分配内存并设置默认的初始值)和解析(将符号引用替换为直接引用)三个步骤。最后JVM对类进行初始化,包括:1)如果类存在直接的父类并且这个类还没被初始化,那么就先初始化父类;2)如果类中存在初始化语句,就依次执行这些初始化语句。

类的加载是由加载器完成的,类加载器包括:根加载器(BootStrap)、扩展加载器(Extension)、系统加载器(System)和用户自定义类加载器(java.lang.ClassLoader的子类)。从Java2(JDK1.2)开始,类加载过程采取了父亲委托机制(PDM)。PDM更好的保证了Java平台的安全性,在该机制中,JVM自带的Bootstrap是根加载器,其他的加载器都有且仅有一个父类加载器。类的加载首先请求父类加载器加载,父类加载器无能为力时才由其子类加载器自行加载。JVM不会向Java程序提供对Bootstrap的引用。

下面是关于几个类加载器的说明:

  • Bootstrap:一般用本地代码实现,负责加载JVM基础核心类库(rt.jar);
  • Extension:从java.ext.dirs系统属性所指定的目录中加载类库,它的父加载器是Bootstrap;
  • System:又叫应用类加载器,其父类是Extension。它是应用最广泛的类加载器。它从环境变量classpath或者系统属性java.class.path所指定的目录中记载类,是用户自定义加载器的默认父加载器。

22、char型变量中能不能存储一个中文字符,为什么?

答:char类型可以存储一个中文字符,因为Java中使用的编码是Unicode,一个char类型占2个字节(16比特),所以放一个中文字符是没问题的。

23、JDK 和 JRE 有什么区别?

答:JDK是Java开发工具包,JRE是Java运行环境。JDK中包含了Java的开发环境和运行环境(例如开发所使用的编译器等)。所以JDK一般为Java开发人员所使用,而JRE只包含运行环境,所以用来运行Java程序。

24、== 和 equals 的区别是什么?

答:1、==判断2个变量或对象实例是否指向同一个内存空间,equals()判断2个变量或对象实例所指向的内存空间的值是否相同。

2、==对内存地址进行比较,equals()对字符串内容进行比较。

3、==判断引用是否相同,equals()判断值是否相同。

总结:

== 在基本数据类型比较时:值内容;引用类型比较时:地址
equals 重写:值内容 , equals不重写:地址

例子:

import java.io.*;
 import java.util.Arrays;public class test{    
     public static void main(String[] args){
         // System.out.println(new Square().area());
         String a = "hello";
         String b = "hello";
         
         System.out.println(a == b);                //true
         System.out.println(a.equals(b));        //true
         
         String c = new String("world");
         String d = new String("world");
         System.out.println(c == d);                //false
         System.out.println(c.equals(d));         //true
     }         
 }

25、两个对象的 hashCode()相同,则 equals()也一定为 true,对吗?

答:

两个对象的hashCode()相同,equals()不一定为true;

两个对象的equals为true,则两个对象的hashcode一定为true;

示例:

public class test{    
     public static void main(String[] args){
         // System.out.println(new Square().area());
         String s1="Ma";
         String s2="NB";
         System.out.println("s1.hashCode()=" + s1.hashCode()); // 2484
         System.out.println("s2.hashCode()=" + s2.hashCode()); // 2484
         System.out.println(s1.hashCode()==s2.hashCode());  //true
         System.out.println(s1.equals(s2));  //false    }         
 }

26、final 在 java 中有什么作用?

答:

未完待续......