美文网首页
常见面试题|Java基础(一)

常见面试题|Java基础(一)

作者: 蓝精灵与大脸猫 | 来源:发表于2020-09-16 00:20 被阅读0次

    基础

    Q:Integer和int的区别

    int是基本数据类型,Integer是类,因为Java很多地方需要类,比如泛型里
    Java有自动装箱和自动拆箱
    对象类都是final的
    -128到127之间,Integer.valueOf返回的是缓存,不新建对象。这个范围可以通过JVM参数设置

    Q:Java和C++的区别

    Java和C++都是面向对象的,都有封装继承和多态
    C++有指针,需要程序员手动释放内存,Java没有指针,不需要手动释放内存
    C++支持多继承,Java不支持多继承

    Q:重写、重载

    重载是一个类里,函数名相同,参数不同,返回值和访问权限不要求
    重写是父类和子类之间,对象方法,static方法不行,是基于对象方法的。父类的方法在子类里必须可见,private不行。要求函数名相同,参数相同,返回值子类小于父类,编译异常子类小于父类,访问修饰符,子类大于等于父类。属性没有覆盖的概念,是一种隐藏。

    Q:重载的方法,参数个数相同,都是引用类型,调用的时候如果每个参数都传入null,会有什么反应?编译报错?运行报错?

    IDE报错,编译不通过

    Q:static方法可以被子类重写吗

    不可以,不能被重写,只能同名隐藏
    父类引用指向子类对象,只会调用父类的静态方法,不具有多态性。可以继承

    Q:初始化

    局部变量(函数内的变量)不初始化会报错,成员变量有默认的初始值,数组对象是null

    Q:instanceof

    对象是不是这个类的对象,返回true或false
    接口和父类都是true

    Q:transient瞬时的

    某成员变量有transient,不参与序列化

    Q:代码块、静态代码块、构造代码块

    代码块:在方法内部,目的是控制变量生命周期,及早释放,避免命名冲突
    静态代码块,类里static{},作用是初始化类里的static变量。
    在jvm加载类的时候执行,只执行一次,在主方法之前
    构造代码块:初始化成员变量,构造方法共同的初始化可以提到构造代码块
    每次创建对象时调用,在构造方法之前

    Q:Object中的方法

    1.clone
    2.toString
    3.hashcode
    4.equals
    5.getclass
    6.wait
    7.notify
    8.notifyAll
    9.finalize

    Q:null

    null可以强制转换成任何类,可以用来调用静态方法
    ((People)null)testMethod();
    对象方法会报空指针

    Q:实参个数可变

    public void test(int....i){}
    不确定个实参,可以是0个,可以不传,是一个数组
    public void test(int[] i)
    会报错,多重定义

    Q:参数传递

    Java是值传递,对象数组是拷贝地址

    Q:实例化方法

    1.new
    2.clone,不调用构造方法
    3.反序列化
    4.反射newInstance,会调用无参的构造方法,没有回报错
    5.工厂方法:String str = String.valueOf(23)

    Q:String

    Java虚拟机中有一个字符串池,是共享的,final的,不用担心改变
    new String()会在堆里分配,而且不会再放到常量池中,要放进常量池需要intern()方法,这个方法放进常量池,返回常量池对象

    Q:字符串内存分配

    String s1 = new String("abc"); // 内存分配
    String s2 = "abc";
    if (s1 == s2) {
    // 能不能进来? n
    }
    String s3 = "abc";
    if (s2 == s3) {
    // 能不能进来? y
    }
    s2 = new String("abc");
    if (s1 == s2) {
    // 能不能进来? y
    }
    s2 = s1;
    s1 = "bcd";
    // s2 = ? ab

    Q:String、StringBuilder、StringBuffer

    String不可变
    StringBuilder可变,但是不线程安全,性能好
    StringBuffer,线程安全

    Q:final、finally、finalize

    • final修饰变量、类、方法,代表不可变,final static 代表常量
      JVM会对方法,变量,类进行性能优化
      final变量必须在声明的时候初始化或者在构造方法初始化
      匿名类中所有变量都必须是final的
    • finally
      异常中使用,代表一定会执行,用来断开连接,释放资源等,return会覆盖try,返回
      除非try中exit或者退出虚拟机了
    • finalize
      finalize 方法是Object方法,在对象被回收前调用,作用是释放c++层的内存等
      如果方法是纯Java写不需要写这个方法,finalize只执行一次,可以对象复活一次
      垃圾回收的时候,判断这个对象是否覆盖了finalize方法,没有覆盖直接回收,有finalize方法,且没有执行过就放入F-Queue队列,由一个线程执行该队列中的finalize方法,执行完垃圾回收器再次判断,是否可达,不可达回收,否则复活
      如果在执行finalize方法时,出现异常,垃圾回收器不会报异常,只会该对象的finalize执行退出

    Q:抽象类和接口的区别

    1.接口没有构造方法,抽象类有构造方法
    2.抽象类可以有普通方法,接口所有方法都是抽象的
    3.抽象类中有普通成员变量,接口没有普通成员变量
    4.接口方法只能是public,抽象类可以是public、protected、默认类型
    5.接口中可以有静态方法,但是必须实现
    接口中可以包含静态成员变量,但是必须是public static final,抽象类都可

    Q:多态

    父类的引用指向不同的子类对象,可以不修改代码,让引用绑定到不同的类
    父类引用可以调用父类中定义的所有属性和方法,只存在子类的方法和属性就不能访问了

    内部类

    Q:为什么需要内部类

    1.通过内部类来隐藏信息
    2.实现多重继承

    Q:内部类为什么可以访问外部类变量

    含有一个外部类的this指针
    内部类分:成员内部类、局部内部类、静态内部类、匿名内部类
    内部类可以无条件访问外部所有属性和方法,包括private和静态成员
    和普通的成员一样
    匿名内部类在new后面,只能生成一个对象
    编译的时候由系统自动起名Outter$1.class
    没有类名,不能写构造方法,编译器会生成一个不带参数的构造方法
    要初始化可以用构造代码块
    不能有静态方法(没有类名怎么调用啊)

    Q:内部类为什么持有外部对象,static内部类为什么没有

    内部类会生成一个私有构造函数,传入外部对象
    class Outer$Inner{
    private Outer&Inner(Outer paramOuter){}
    }
    这是静态内部类编译后的字节码文件,编译器并没有为它添加额外的构造函数,所以它其实和我们的外部类没有任何关系,这是写在同一个.java源文件中而已.

    Q:匿名内部类使用的参数为什么必须是final的

    匿名内部类编译后也是单独一个class文件,仅仅有一个外部类的引用,外部传入参数,其实是参数的拷贝,内部的修改并不影响外部。这样从程序员角度看是同一个,内部改变了外部确没变,为了保持一致,所以规定用final避免形参可变。
    内部类,实际会在匿名匿名内部类的构造方法中拷贝一份要使用的外部变量。这样,就牵扯两份变量的同步问题,比如内部类实例被回收、匿名内部类被实例化之前外部变量被更改等问题。解决该问题的方法,就是强制使用final,保证引用不可变

    Q:内部类为什么可以访问外部private变量

    Q:静态内部类和非静态的区别

    静态内部类和静态成员变量类似
    不能使用外部非静态成员变量或方法
    普通内部类都可以方法
    静态内部类可以声明普通成员变量和方法,普通内部类不能声明static成员变量和方法
    静态内部类可以单独初始化,不依赖于外部
    Inner i = new Outer.Inner()
    //普通内部类初始化
    Outer o = new Outer();
    Inner i = o.new Inner();

    Q:枚举

    枚举也是类,都继承了Enum,每一个变量是类的对象,还是static final修饰的
    构造方法是private,没有办法创建枚举值。可以有成员变量,成员方法
    public enum Color{ RED,BLUE,BLACK }
    编译后
    final enum Color{
    public static final Color RED;
    public static final Color BLUE;
    //静态内部类,对对象初始化
    static{
    new Color[1]
    }
    private Color(){}
    }
    继承Enum方法
    (1)ordinal():返回枚举的顺序
    Color.RED.ordinal(); 返回0
    (2)compareTo():比较两个枚举的顺序
    (3)values()返回全部枚举的数组
    Color[] colors = Color.values();
    (4)toString()返回常量的名
    Color c = Color.RED;
    System.out.println(c); 返回RED
    (5)valueOf,返回这个名词的常量
    Color.valueOf(“BLUE”); 返回Color.BLUE

    Q:IO

    字节流InputStream/OutputStream
    字符流 Reader/Writer
    字符流是字节流读取时查了指定的码表
    字节流以字节(8bit)为单位,字符流以字符为单位
    字节流能处理所有类型的数据,如图片、AVI,字符流只能处理字符数据
    优先使用字节流

    反射

    Q:什么是反射

    反射机制指的是程序在运行时能够获取自身的信息
    在Java中,给定类的名字,那么就可以通过反射机制来获得类的所有信息。
    Class.forName("com.mysql.jdbc.Driver.class").newInstance();
    hibernate、struts都是用反射机制实现的。(android蓝牙)

    Q:为什么要有反射,反射是怎么解决这个问题的,Android里如何用的项目里你是如何用反射的

    反射可以在运行的时候动态的加载,而不是固定的,增加了灵活性
    比如实例化一个对象new Person(),如果想实例化其他的对象,不想修改源码,就可以用反射class.forName(“person”).newInstance(),把加载哪个类写到配置文件中

    Q:三种获取Class对象方法

    1.Class的静态方法 Class.forName(“java.lang.String”);
    2.使用类的.class语法 String.class;
    3.对象的getClass()方法
    String str = “aa”;
    Class<?> classType = str.getClass();

    Q:反射创建对象,获取构造器

    不带参的构造方法生成对象有两种方式
    1.newInstance()直接生成即可,调用无参构造方法
    Class<?> clazz = String.class;
    Object obj = clazz.newInstance();
    2.用Constructor的构造方法调用newInstance(参数)
    Class<?> clazz = Customer.class;
    //获得无参构造方法
    Constructor cons = clazz.getConstructor(new Class[]{});
    //通过构造方法来生成对象,构造方法调 con.newInstance()
    Object obj = cons.newInstance(new OBject[]{}); //传入参数
    如果要通过带参的构造方法生成对象,只有一种方式
    Class<?> clazz = Customer.class;
    Constructor cons2 = clazz.getConstructor(new Class[]{String.class,int.class});
    Object obj2 = cons2.newInstance(new Object[]{“zhangsan”,20});
    私有构造方法加一个cons2.setAccessible(true);
    Teacher clazz =Teacher.class;
    Constrctor cons = clazz.getConstructor(String.class);
    Teacher t = (Teacher)cons.newInstance(“aa”);

    Q:反射获取私有属性并修改

    1.获得CLass
    2.创建对象
    3.getField或getDeclaredField获取Field
    4.修改
    Class<?> clazz = Class.forName("com.ang.Teacher");
    通过有参构造获取对象,有参构造必须为public类型
    Constructor c = clazz.getConstructor(String.class,int.class);//字节码阶段,构造方法的参数只能是字节码对象;
    Teacher t = (Teacher) c.newInstance("王艳",100); //创建实例对象
    Field field = clazz.getDeclaredField("name");//获取私有成员变量
    field.setAccessible(true);//去除私有权限
    field.set(t,”李代理”); //传入对象和这个属性的新值

    Q:反射如何调用私有方法

    Class<?> clazz = Class.forName("com.ang.Teacher");
    Constructor c = clazz.getDeclaredConstructor(); //获取私有无参构造
    c.setAccessible(true); //去除私有权限
    Teacher t = (Teacher) c.newInstance();//通过私有构造获取实例
    Method method = clazz.getDeclaredMethod("add", int.class,int.class);//字节码阶段反射有参方法是,需传入参数类型的字节码
    method.setAccessible(true); //去除私有权限
    int num = (Integer) method.invoke(t, 20,8); //invoke第一个是类的实例,第二个是参数
    如果是静态方法,第一个参数,传入null
    method.invoke(null,20,8);
    getMethods()获得该类所有公有的方法,getDeclaredMethod(parameterTypes)获得该类某个方法,getDeclaredMethods()获得该类所有方法。带有Declared修饰的方法可以反射到私有的方法,没有Declared修饰的只能用来反射公有的方法。其他的Annotation、Field、Constructor也是如此

    Q:final成员变量修改

    Java反射可以修改final成员变量吗?
    分别两种情况
    1.在定义的时候就初始化了值,private final String name = “huang”;因为编译器final类型的数据自动被优化了,所有用到的地方都替换成立常量,所以是return “huang”,而不是return this.name。所以不能修改,向阻止编译自动优化,可以改为final String name = (null!=null?”ddd”:”huang”)
    2.如果是在构造函数初始化,可以修改
    final Class<?> clz = p.getClass();
    final Field nameField = clz.getDeclaredField("name");
    nameField.setAccessible(true);
    nameField.set(p, String.valueOf("huang.damon"));

    equals、hashcode

    Q:equals、hashcode

    为了提高效率,先判断hashcode,hashcode不相同,equals肯定不相同,就不用算了
    如果hashcode相同,再判断equals。这样实际调用equals的次数就大大减少了
    在HashSet中的使用,set不允许有相同的对象,就先hashcode,在equals,相同就不存
    如果你的类不放在hashcode为基础的容器就不需要重新hashcode
    但是如果要放到hashset或者hashmap,他们equals相同,hashcode必须相等
    result = 29*getName().hashCode()+getBirthday.hashCode();

    Q:怎么重写hashcode方法,String的hashcode方法

    用equals中涉及到的属性来计算,equal相同的hashcode要相同
    首先定义个初始值,一般来说取17
    然后根据属性类型解析
    1.boolean: hashcode = a?1:0
    2.int,byte,shot,char : hashcode = (int)b
    3.long: hashcode = c^c>>>32
    4.float: hashcode = d.hashCode()
    5.double: hashcode = e.hashCode()
    6.引用:若为null则为0,否则递归调用引用的hashcode
    7.数组,String:s[0]31 ^ (n-1) + s[1] * 31 ^ (n-2) + ..... + s[n-1]
    第三步
    result = result
    31 +hashcode

    Q:自定义重写equals的同时还需要重写哪个方法

    一般应该重写equals()还有hashCode()

    Q:如果没有重写hashcode会发生什么

    有些集合是不允许重复元素出现的,这两个方法用来保证集合中没有重复元素
    如果只重写了equals方法,两个对象equals返回了true,但是没有重写hashcode方法
    集合还是会插入元素,这样集合中就出现重复元素了
    hashMap的put方法是先调用hashCode定位到数组的位置,如果该数组的位置上已经存在元素了,即table[i]!=null,那么遍历链表,调用equals方法判断key是否相等,如果equal不相同,说明没有找到这个key,表明这个key不存在,则会插入
    如果没有重写hashcode方法,那么就无法定位到同一个数组位置,集合还是会插入元素,这样集合中就出现重复元素了,重写equals就没有意义了
    如果重写了hashcode就能定位到数组相同的位置,就可以遍历这条单向链表,用equals判断这两个对象是否相同,如果相同就覆盖,不相同,就插入到链表的头节点处

    Q:为什么要有拷贝?解决了什么问题,怎么解决的,什么场景使用

    从一个已有的对象,创建一个相似或相同的对象,是模板设计模式,简化对象的创建
    值直接拷贝,引用也直接拷贝,不行,怎么办?引用也实现拷贝,遍历值拷贝
    或者先把对象序列化,再反序列化,是另一个对象了
    浅拷贝:值直接拷贝,引用和对象,拷贝的地址,指向同一块内存,没有新开辟地址
    深拷贝:引用和对象新开辟地址,也全部拷贝
    clone方法是浅拷贝,implements Cloneable
    要实现深拷贝
    1.类里的每一个对象都重新clone方法,在顶层类,调用所有对象的clone方法
    age是一个成员变量,age里实现clone方法,Student里有age,再调用age的clone方法
    2.通过对象的序列化实现深拷贝
    先把这个对象序列化,再反序列化给另一个对象
    oos.writeObject(stu1);
    Student stu2 = (Student)ois.readObject():
    clone()默认是深拷贝还是浅拷贝,如何让clone实现深拷贝

    相关文章

      网友评论

          本文标题:常见面试题|Java基础(一)

          本文链接:https://www.haomeiwen.com/subject/tpoqyktx.html