Java面试题——基础篇

1、Java支持的数据类型有哪些?什么是自动装箱和拆箱?        

     装箱是将值类型转换成Object引用类型,拆箱是已被装箱的引用类型转换成原来的值类型。也就是说自动装箱和拆箱就相当于Java编译器在基本数据类型和对应的对象包装类型两者之间的一个相互转化。比如:把int类型转化成Integer类型、double转化成Double类型就是一个自动装箱的过程,反之就是自动拆箱。

      数据类型分成两大类,一种是基本数据类型;另一种是引用数据类型。这两个类型的本质区别是:值类型数据分配在栈中,而引用数据类型分配在堆上。那么如果要把一个值类型数据放在堆上,就需要装箱的操作;反之,把一个放在堆上的值类型数据取出来,则需要进行拆箱的操作,频繁的拆箱和装箱操作会降低程序效率,因此在编写的时候要尽量避免。   

2、JDK、JER、JVM分别是什么?

       JDK(Java Development Toolkit):Java开发工具包,是程序员使用语言编写Java程序所需要的开发工具包,是提供给程序员使用的。JDK包含了JRE,同时还包含了编译Java源码的编译器javac,还包含了很多Java程序调试和分析的工具:jconsole,jvisualvm等工具软件,还包含了Java程序编写所需要的文档和demo例子程序。

      JRE(Java Runtime Enviromental):Java运行时环境,包含了jvm,Java基础类库,是使用Java语言编写的程序运行所需要的软件环境,同时也包含了执行Applet需要的浏览器插件,是提供给想运行Java程序的用户使用的。

     JVM(Java Virtual Mechinal):Java虚拟机,是一个可以执行Java字节码的虚拟机进程。Java源文件被编译成能被Java虚拟机执行的字节码文件。Java被设计成允许应用程序可以运行在任意的平台,而不需要程序员为每一个平台单独重写或者重新编译。Java虚拟机让这个变为可能,因为它知道底层硬件平台的指令长度和其他特性。

3、什么是值传递和引用传递?

       值传递(形式参数是基本数据类型):方法调用时,实际参数把它的值传递给对应的形式参数,形式参数只是实际参数的值初始化自己的存储单元内容,是两个不同的存储单元,所以方法执行中形式参数值的改变不影响实际参数的值。也就是说,对象被值传递,意味着传递了对象的一个副本。因此,就算是改变了对象的副本,也不会影响原对象的值。

      引用传递(形式参数类型是引用数据类型参数):也称为地址传递。方法调用时,实际参数是对象或数组,这时实际参数与形式参数指向同一个地址,在方法执行中,对形式参数的操作实际上就是对实际参数的操作,这个结果在方法结束后被保留了下来,所以方法执行中形式参数的改变将会影响实际参数。也就是说:对象被引用传递,意味着传递的并不是实际的对象,而是对象的引用。因此,外部对引用对象所做的改变会反映到所有的对象上。

4、int和integer有什么区别?

        Java是一个近乎纯洁的面向对象编程语言,但是为了编程的方便还是引入了基本数据类型,但是为了能将这些基本数据类型当成对象操作,Java为每数据类型都引入了对应的包装类型(wrapper class), int的包装类就是Integer,从Java5开始引入自动装箱/拆机制,使得二者可以相互转化.

        Java为每个原始类型提供了包装类型:

           ——原始类型:boolean、char、byte、short、int、long、float、double

           ——包装类型:Boolean、Character、Byte、Short、Integer、Long、Float、Double

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

      方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。

      重载:发生在同一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不相同),方法的返回值和访问权限修饰符可以不同。
 

 public void fun(){
        System.out.println("The first method.");
    }
    
    public void fun(Integer x){
        System.out.println("asd");
    }

       重写:发生在子类与父类之前,重写要求被重写方法与父类被重写方法有相同的返回值类型,比父类被重写方法更好访问,不能比重写方法声明更多的异常(历史替换原则)。也就是说:方法名、参数列表必须相同访问权限修饰符大于等于父类,父类方法修饰不能用private修饰。

public class Child extends Parent{
    @Override
    public void fun() {
        super.fun();
    }

    @Override
    public void fun(Integer x) {
        super.fun(x);
    }
}

6、String、StringBuffer、StringBuilder的区别?

      Java提供了两种类型的字符串:String和StringBuffer/StringBuilder,它们可以存储和操作字符串,其中String是只读字符串,也就意味着String引用的字符串内容是不能被改变的。而StringBuffer/StringBuilder类表示字符串对象可以直接进行修改。StringBuilder是Java5中引入的,它和StringBuffer的方法完全相同,区别在于它是单线程环境下使用的,因为它的所有方面都没有被synchronized修饰,因此它的效率也比StringBuffer要高。

1 String str="abc"+"de";
2 StringBuilder stringBuilder=new StringBuilder().append("abc").append("de");
3 System.out.println(str);
4 System.out.println(stringBuilder.toString());

这样输出结果也是“abcde”和“abcde”,但是String的速度却比StringBuilder的反应速度要快很多,这是因为第1行中的操作和

  String str="abcde";

  是完全一样的,所以会很快,而如果写成下面这种形式

1 String str1="abc";
2 String str2="de";
3 String str=str1+str2;

 那么JVM就会像上面说的那样,不断的创建、回收对象来进行这个操作了。速度就会很慢。

在线程安全上,StringBuilder是线程不安全的,而StringBuffer是线程安全的

  如果一个StringBuffer对象在字符串缓冲区被多个线程使用时,StringBuffer中很多方法可以带有synchronized关键字,所以可以保证线程是安全的,但StringBuilder的方法则没有该关键字,所以不能保证线程安全,有可能会出现一些错误的操作。所以如果要进行的操作是多线程的,那么就要使用StringBuffer,但是在单线程的情况下,还是建议使用速度比较快的StringBuilder。

  3. 总结一下
  String:适用于少量的字符串操作的情况

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

  StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况

7、ArrayList和LinkedList的区别?

      1、ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构

      2、 对于随机访问get/set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。

      3、对于新增和删除操作add和remove,LinkedList比较占优势,因为ArrayList要移动数据。

     简单点说就是:ArrayList增删效率高查询效率低;LinkedList增删效率低查询效率高。

package com.huadian;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class ALList {
   static final int N = 50000;
   static long timeList(List list){
       long start = System.currentTimeMillis();
       Object o = new Object();
       for (int i = 0;i< N;i++){
            list.add(0,o);
       }
       return System.currentTimeMillis()- start;
   }

   static long readList(List list){
       long start = System.currentTimeMillis();
       for (int i = 0,j=list.size();i<j;i++){

       }
       return System.currentTimeMillis()-start;
   }

    static List addList(List list){
        Object o = new Object();
        for(int i=0;i<N;i++) {
            list.add(0, o);
        }
        return list;
    }
    public static void main(String[] args) {
        System.out.println("ArrayList添加"+N+"条耗时:"+timeList(new ArrayList()));
        System.out.println("LinkedList添加"+N+"条耗时:"+timeList(new LinkedList()));
        List list1=addList(new ArrayList<>());
        List list2=addList(new LinkedList<>());
        System.out.println("ArrayList查找"+N+"条耗时:"+readList(list1));
        System.out.println("LinkedList查找"+N+"条耗时:"+timeList(list2));
    }
}

8、什么是抽象?

      从特定的角度出发、从已经存在的一些事物中抽取我们所关注的特性、行为,从而形成一个新的事务的思维过程,是一种从复杂到简洁的思维方式。

      看下图,我们首先关注猫的特征,比如形态、毛的颜色、它的眼神等等,从而得出猫,这就是抽象。

package com.huadian.InterviewQuestions;

public abstract class Abstract {

    protected int bottom; //底
    protected int height; //高
    public Abstract(int bottom,int height){
        this.bottom = bottom;
        this.height = height;
    }
    public abstract void perimter(); //周长
    public abstract void area();    //面积
}


package com.huadian.InterviewQuestions;

public class AbstractParent extends Abstract {

    public AbstractParent(int bottom, int height) {
        super(bottom, height);
    }

    @Override
    public void perimter() {
        int perimeter = (bottom*height)*2;
        System.out.println(perimeter);
    }

    @Override
    public void area() {
        int area = bottom*height;
        System.out.println(area);
    }
}

9、Bog b = new Bog();的含义?和 b = new Bog();的区别?

       Java对象的创建有几步:

            1、声明对象:Bog b = null;

            2、为对象赋值,也就是分配内存空间 b = new Bog();

       其实Bog b = new Bog();和Bog b = null; b = new Bog();是一个意思,前者是后者的简单写法而已。

           但是,要知道的是:

                     Bog b;表示声明了一个对象b,他的类型为Bog,但是它只是一个引用,并没有实体。

                    new Bog(); 表示新建了一个类型为Bog的实体。

                    Bog b = new Bog();新建一个类型为Bog的实体对象,并将它命名为b,b指向新建的这个对象。

       b = new Bog(); 这种是不能单独存在的,前面必然申明了b的类型,比如: Person p = new Person("张三"),这时候p表示"张三",然后 p = new Person("李四"),这时候p就不再指向"张三"了,而是指向“李四"。

10、堆和栈有什么区别?

        堆和栈都是Java用来存放数据的地方。值得一提的是除了这两者之外还有一个方法。栈的存取速度比较快,但是堆可以动态的分配内存大小。

        栈内存:栈内存首先是一片内存区域,存储的都是局部变量,凡是定义在方法中的都是局部变量,for循环内部定义的也是局部变量,实现加载函数才能进行局部变量的定义,所以方法先进栈,然后在定义变量,变量有自己的作用域,一旦离开作用域,变量就会被释放。栈内存的更新速度很快,因为局部变量的生命周期都很短。

        堆内存:存储的是数组和对象(其实数组就是对象),凡是new建立的都是在堆中,堆中存放的都是实体(对象),实体用于封装数据,而且是封装多个(实体的多个属性),如果一个数据消失,这个实体也没有消失,还可以用,所以堆是不会随时释放的,但是栈不一样,栈里存放的都是单个变量,变量被释放了,那就没有了。堆里的实体虽然不会被释放,但是会被当成垃圾,Java有垃圾回收机制不定时的收取。

 所以堆与栈的区别很明显:

            1.栈内存存储的是局部变量而堆内存存储的是实体;

            2.栈内存的更新速度要快于堆内存,因为局部变量的生命周期很短;

            3.栈内存存放的变量生命周期一旦结束就会被释放,而堆内存存放的实体会被垃圾回收机制不定时的回收。

public   class  AppMain {               //运行时, jvm 把appmain的信息都放入方法区    
    
public   static   void  main(String[] args){  //main 方法本身放入方法区。    
    
Sample test1 = new  Sample( " 测试1 " );   //test1是引用,所以放到栈区里, Sample是自定义对象应该放到堆里面    
Sample test2 = new  Sample( " 测试2 " );    
  
test1.printName();    
test2.printName();    
}    
}
Sample.java    
  
public   class  Sample {       //运行时, jvm 把appmain的信息都放入方法区    
    
/** 范例名称 */    
private  name;      //new Sample实例后, name 引用放入栈区里,  name 对象放入堆里    
  
/** 构造方法 */    
public  Sample(String name){     
   
this .name = name;    
}    
  
/** 输出 */    
public   void  printName(){    //print方法本身放入 方法区里。    
   
System.out.println(name);    
}    
}   
发布了88 篇原创文章 · 获赞 383 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/weixin_42047611/article/details/81624128
今日推荐