Java面试题

一定会的题

3.hashCode()相同,equals()也一定为true吗?
5.什么是装箱?什么是拆箱?装箱和拆箱的执行过程?常见问题?
6.final在java中的作用(围绕修饰类、变量、方法三个方面回答)
8.final、finally、finalize()的区别?
9.finally语句块一定会执行吗?
10.return与finally的执行顺序对返回值的影响
11.String中replace和replaceAll的区别
12.Math.round(-1.5)等于多少?
13.java中操作字符串的类有哪些?它们之间有什么区别?
14.如何将字符串反转(StringBuilder、遍历、递归)
17.抽象类能被final修饰吗?
19.Java的访问修饰符有哪些,权限是?
20.Java中的<<、>>、>>>是什么?
24.什么是JAVA内部类?
26.什么是反射?反射的作用?
29.什么是java序列化?什么情况下需要序列化?
30.什么场景要对象克隆?

没有把握的题

1.JDK与JRE有什么区别
2.== 和 equals的区别是什么?(先总的概述、再分别介绍==和equals的作用)
4.基本数据类型和包装对象使用==和equals进行比较的结果?(整型的缓存)
7.final与static的区别(围绕类、方法、变量回答)
15.字符串中常用的方法有哪些
16.普通类和抽象类有什么区别?
18.接口和抽象类的区别
21.javap的作用是什么?
22.throw和throws的区别?
23.常见的异常类有哪些?
25.nio中的Files类常用方法有哪些?
27.动态代理是什么?应用场景?
28.怎么实现动态代理

重要

45.说一说你的对面向过程和面向对象的理解

1.JDK与JRE有什么区别?

1.JRE(Java Runtime Environment):Java运行时环境,包含lJV了M,和Java基础类库;
2.JDK(Java Development Kit):Java开发工具包,即Java语言编写的程序所需的开发工具包;包含了JRE,编译工具javac,监控工具jconsole,分析工具jvisualvm等;


2.== 和 equals的区别是什么?

首先总的概述:

==是关系运算符,equals()是方法,结果都返回布尔值;
Object的==和equals()比较的都是地址值,作用相同

==的作用:

基本数据类型:比较值是否相等
引用数据类型:比较地址值是否相等
不能比较没有父子关系的两个对象

equals()的作用:

JDK中的类(Java基础类库)一般已经重写了equals方法,比较的是内容;
自定义类如果没有重写equals(),将调用父类(默认是Object)中的equals()方法,Object中的equals()使用this == obj,判断两个对象是否相等;
可以按照需求,重写对象的equals()方法;重写对象的equals方法,一般需要重写hashCode()方法


3.hashCode()相同,equals()也一定为true吗?

答案肯定是不一定的。同时反过来equals()返回true时,hashCode()也不一定相同;

  1. 类的hashCode()方法和equals()方法都可以重写,返回值完全在于自己定义。

关于hashCode()和equals()方法有一些规约

  1. 两个对象的equals()比较返回true时,那么两个对象的hashCode()方法必须返回相同的结果
  2. 两个对象的equals()比较返回false时,那么两个对象的hashCode()方法不要求返回不同的值,但是最好不同

4.基本数据类型和包装对象使用==和equals进行比较的结果?

  1. 值不相同的情况下,无论使用==还是equals()比较都返回false;
  2. 值相同的情况下:
    1. 使用==比较:基本类型和基本类型、基本类型和包装对象都返回true;
    2. 包装对象和包装对象比较(地址比较),如果地址值不相同就返回false;
      特殊情况:当使用自动装箱得到的Short、Integer、Long对象时,会存在一个缓存(类似字符串常量池的东西),会将-128~127保存在缓存数组中(静态的),自动装箱的数据值在这个范围内时,引用的都是这个静态数组中的值,所以使用==比较会返回true;
/*
    基本数据类型和包装类对象使用==和equals进行比较
     */
    @Test
    public void test1() {
        /*
        1.值不相同的情况下,使用==和equals()比较都会返回false

        2.值相同的情况下:
            2-1.使用==比较:基本类型和基本类型、基本类型和包装对象都返回true
                包装对象和包装对象比较(地址比较),如果地址值不相同就返回false;
                特殊情况:当使用自动装箱得到的Short,Integer,Long对象时,
                        会存在一个缓存(类似字符串常量池的东西),会将-128~127保存在Integer类的静态域中,
                        自动装箱的数据值在这个返回内引用的都是这个静态域中的值,
            2-2.使用equals()比较,在值相同的情况下使用equals比较,任何情况下都返回true;
        3.不同类型的对象比较,返回false;Byte和Integer比较
         */

        int num1 = 200;
        Integer num2 = 200;     //自动装箱

        System.out.println("num1==num2 = " + (num1 == num2));

        Integer num3 = new Integer(100);
        Integer num4 = new Integer(100);
        System.out.println("(num3 == num4) = " + (num3 == num4));

        //Integer类型的常量池
        Integer num5 = 100;
        Integer num6 = 100;
        System.out.println("(num5 == num6) = " + (num5 == num6));
    }

    //说明Short也存在常量池
    @Test
    public void test2() {
        short b1 = 127;
        Short b2 = 127;
        Short b3 = 127;

        System.out.println("(b1 == b2) = " + (b1 == b2));
        System.out.println("(b1 == b3) = " + (b1 == b3));
        System.out.println("(b2 == b3) = " + (b2 == b3));

        System.out.println("----------------------------------");
        b1 = 128;
        b2 = 128;
        b3 = 128;

        System.out.println("(b1 == b2) = " + (b1 == b2));
        System.out.println("(b1 == b3) = " + (b1 == b3));
        System.out.println("(b2 == b3) = " + (b2 == b3));
    }

5.什么是装箱?什么是拆箱?装箱和拆箱的执行过程?常见问题?

  1. 什么是装箱?什么是拆箱?
    装箱:基本数据类型转变为包装类对象的过程;
    拆箱:包装类对象转变为基本数据类型的过程;
//JDK 1.5之前是不支持自动装箱和自动拆箱的;
Integer num1 = new Integer(127);

//JDK 1.5开始提供了自动装箱的功能;
Integer num2 = 127;

int num3 = num2;	//自动拆箱
  1. 装箱和拆箱的执行过程?
    装箱调用的是包装类的valueOf()方法返回对象
    拆箱调用的是包装类的xxxValue()方法返回基本数据类型;

  2. 常见问题

    1. 整型的包装类在自动装箱的时候会存在一个缓存(类似字符串常量池);
    2. 布尔型的包装类valueOf()方法Boolean类的静态常量TRUE|FALSE
Boolean aBoolean1 = Boolean.valueOf(Boolean.TRUE);
Boolean aBoolean2 = Boolean.valueOf(Boolean.FALSE);

6.final在java中的作用

final语义是不可变的;
final可以修饰类、方法、变量

  1. final修饰类时,该类不能被继承;类中的方法、属性也默认都是final的
  2. final修饰成员变量时,成员变量必须初始化,赋值之后就不能再改变了(可以声明时初始化,也可以在构造器中初始化)
    注意:如果在构造器中初始化则必须初始化该变量,类中不能存在没有初始化该变量的构造器,否则就会报错
  3. 被final修饰,对基本数据类型来说值不可变,对引用数据类型来说地址值不可变;
    注意:这里可以扩展一个程序分析类的面试题
    4.final修饰的方法不能被重写;
public class Person {
    private String name;
    private final int age;
	//这个age声明为了final,而空参构造器中没有初始化age,上面的声明就会报错
    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

7.final与static的区别

总的描述:

  1. 都可以修饰类、方法、成员变量
  2. 都不能修饰构造方法
  3. static可以修饰代码块,而final不能
  4. final可以修饰局部变量,而static不能

static:

  1. static修饰表示静态或全局,被修饰的属性和方法属于类,可以直接通过类名访问
  2. static修饰代码块时表示静态代码块,只有当JVM虚拟机加载类的时候,执行且仅执行一次;
  3. static修饰的成员变量,即类变量,是在类加载的时候创建并初始化,只会被创建一次
  4. static修饰的方法中不能使用this或super关键字
  5. static方法必须被实现,而不能是抽象的,因为static修饰的方法,不能被重写,在类加载的时候就会被解析,如果是abstract的就会报错
  6. static修饰的方法不能被重写(其实可以,但是不能算作是重写了,重载吧)

final:

  1. final修饰的变量即常量,不但创建不可修改;
  2. final修饰的成员变量必须在声明的时候初始化,或者在构造器中初始化,类中不能存在没有初始化常量成员变量的构造器;
  3. final修饰的方法不能被重写;
  4. final修饰的类不能被继承,final类中的方法默认都是final的;

8.final、finally、finalize()的区别?

  1. final:表示最终的、不可变的。用于修饰类、方法、变量
  2. finally:是异常处理的一部分,它只能用在try-catch语句中,finally语句块中的代码一定会执行(存在特殊情况导致finally不会被执行
  3. finalize()是在java.lang.Object中定义的,Object的finalize()什么也不做,只是对象被回收之前调用finalize方法;
    我们在自定义类的时候,可以重写finalize()方法:在对象被回收之前释放一些需要手动释放的资源,需调用super.finalize();

9.finally语句块一定会执行吗?

不一定!!!

  1. 直接返回未执行到try-finally语句块;
    注意:在try中return时,也会执行finally语句块
    这里,联想到try-finally中对return的值进行修改的影响
  2. 抛出异常未执行到try-finally语句块;
    在try中抛异常,依旧会执行finally语句块;
  3. 系统退出未执行到finally语句块;

10.return与finally的执行顺序对返回值的影响

try和finally中至少有一个语句块包含return语句:

  1. finally一定会被执行;
  2. finally没有return语句,那么finally中对return的变量的重新赋值、修改是无效的
  3. try和finally都包含return时,return的变量值会以finally语句块中return的值为准
@Test
    public void test3() {
        System.out.println("getString1() = " + getString1());
        System.out.println("getString2() = " + getString2());
    }
    public String getString1(){
        String str = "A";
        try{
            str = "B";
            return str;
        }finally {
            System.out.println("finally fail to change string to C");
            str = "C";
        }
    }
    public String getString2(){
        String str = "A";
        try{
            str = "B";
            return str;
        }finally {
            System.out.println("finally succeed to change string to C");
            str = "C";
            return str;
        }
    }

11.String中replace和replaceAll的区别

replace()方法:支持字符和字符串的替换,查找到字符串中所有匹配到的字符(串),将其全部替换

replaceAll()方法:基于正则表达式的字符串替换

String str = "Hello! java. java is a language.";
System.out.println(str.replace("java", "c++"));
System.out.println(str.replaceAll("java.", "c++"));

输出结果
Hello! c++. c++ is a language.
Hello! java. java is a language.


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

运行结果是:-1;

JDK中的java.lang.Math类

  • ceil():向上取整,返回坐标上靠近正无穷的整数;
  • floor():向下取整,返回坐下上靠近负无穷的整数;
  • round():参数+0.5,向下取整即可得到值;

13.java中操作字符串的类有哪些?它们之间有什么区别?

String、StringBuffer、StringBuilder

  1. String是不可变序列;底层使用final char[]存储,所有对String字符串的操作,都不会影响原字符串,都会返回一个新的字符串
  2. StringBuffer是可变序列;线程安全的,效率低;底层使用char[]存储
  3. StringBuilder是可变序列,jdk5.0新增的,线程不安全的,效率高;底层使用char[]存储

可以扩展StringBuilder的源码分析


14.如何将字符串反转

可以使用StringBuilder中的reverse()方法
自己实现(递归、遍历)

@Test
public void test3() {
     String str = "abcdefg";
     System.out.println("使用StringBuilder的reverse()方法");
     System.out.println(new StringBuilder(str).reverse().toString());
     System.out.println("使用遍历反转字符串");
     System.out.println(reverseString(str));
     System.out.println("使用递归反转字符串");
     System.out.println(recursionString(str));
 }

 public String reverseString(String str) {
     int len = str.length();
     char[] chars = new char[len];

     for (int i = len - 1; i >= 0; i--) {
         chars[len - 1 - i] = str.charAt(i);
     }
     return new String(chars);
 }

 public String recursionString(String str){
     if (str == null || str.length() <= 1){
         return str;
     }
     return reverseString(str.substring(1)) + str.charAt(0);
 }

15.字符串中常用的方法有哪些

常用方法

  1. int length():返回字符串的长度: return value.length
  2. char charAt(int index): 返回某索引处的字符return value[index]
  3. String toLowerCase():使用默认语言环境,将 String 中的所有字符转换为小写
  4. String toUpperCase():使用默认语言环境,将 String 中的所有字符转换为大写
  5. String trim():返回字符串的副本,忽略前导空白和尾部空白
  6. boolean equals(Object obj):比较字符串的内容是否相同
  7. boolean equalsIgnoreCase(String anotherString):与equals方法类似,忽略大小写
  8. String concat(String str):将指定字符串连接到此字符串的结尾。 等价于用“+”  int compareTo(String anotherString):比较两个字符串的大小
  9. String substring(int beginIndex):返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后的一个子字符串。
  10. String substring(int beginIndex, int endIndex) :返回一个新字符串,它是此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串。

判断类方法

  1. boolean endsWith(String suffix):测试此字符串是否以指定的后缀结束
  2. boolean startsWith(String prefix):测试此字符串是否以指定的前缀开始
  3. boolean startsWith(String prefix, int toffset):测试此字符串从指定索引开始的子字符串是否以指定前缀开始
  4. boolean contains(CharSequence s):当且仅当此字符串包含指定的 char 值序列时,返回 true
  5. boolean isEmpty():判断是否是空字符串:return value.length == 0

获取索引值

  1. int indexOf(String str):返回指定子字符串在此字符串中第一次出现处的索引
  2. int indexOf(String str, int fromIndex):返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始
  3. int lastIndexOf(String str):返回指定子字符串在此字符串中最右边出现处的索引
  4. int lastIndexOf(String str, int fromIndex):返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索
  5. 注:indexOf和lastIndexOf方法如果未找到都是返回-1

匹配替换类方法

  1. String replace(char oldChar, char newChar):返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。
  2. String replace(CharSequence target, CharSequence replacement):使用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串。
  3. String replaceAll(String regex, String replacement) :使用给定的replacement 替换此字符串所有匹配给定的正则表达式的子字符串。
  4. String replaceFirst(String regex, String replacement) :使用给定的replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。
  5. boolean matches(String regex):告知此字符串是否匹配给定的正则表达式。
  6. String[] split(String regex):根据给定正则表达式的匹配拆分此字符串。
  7. String[] split(String regex, int limit):根据匹配给定的正则表达式来拆分此字符串,最多不超过limit个,如果超过了,剩下的全部都放到最后一个元素中。

16.普通类和抽象类有什么区别?

  1. 抽象类不能被实例化
  2. 抽象类可以有,也可以没有抽象方法,抽象方法无需实现
  3. 含有抽象方法的类必须声明为抽象类
  4. 抽象类的子类必须实现抽象类中的所有抽象方法,否则该类也必须声明为抽象类
  5. 抽象方法不能被static、final、private修饰

17.抽象类能被final修饰吗?

不能,因为抽象类不能实例化,必须通过继承它的子类,进行实例化,所有不能被final修饰


18.接口和抽象类的区别

  1. 抽象类中可以有构造方法,接口中不可以有
  2. 抽象类中可以有普通、静态成员变量,接口中只能是public static final的成员变量
  3. 抽象类中可以有非抽象方法;接口:JDK1.8以前都默认是抽象的,JDK1.8以后可以有default和static方法
  4. 抽象类中抽象方法的访问权限可以是public、protected、default;接口中抽象方法默认是public abstract;
  5. 抽象类可以包含静态方法;接口:在JDK1.8以后可以有静态方法(但必须是已实现的)
  6. 一个类可以实现多个接口,但只能继承一个抽象类
  7. 接口不可以实现多个接口,但能继承多个接口

19.Java的访问修饰符有哪些,权限是?

public:

  • public修饰成员变量、方法时:可以在任何类中都能被访问到
  • public修饰类:在一个Java源文件中,只能有一个public类,且源文件名和这个类名必须一致;

protected:

  • 被protected修饰的成员可以在同一个包内被访问到,也可被子类继承;

friendly:

  • 默认,缺省的;同一个包中的所有类都能访问到;只能被同一个包中的子类所继承

private:

  • 只能在当前类中被访问到

在这里插入图片描述


20.Java中的<<、>>、>>>是什么?

>>:左移,低位补0;
<<:右移,如果该数为正,则高位补0;为负,则高位补1
>>>:无符号右移,也叫逻辑右移,高位补0;


21.javap的作用是什么?

javap 是 Java class文件分解器,可以反编译,也可以查看 java 编译器生成的字节码等。

javap -help
用法: javap
其中, 可能的选项包括:
-help --help -? 输出此用法消息
-version 版本信息
-v -verbose 输出附加信息
-l 输出行号和本地变量表
-public 仅显示公共类和成员
-protected 显示受保护的/公共类和成员
-package 显示程序包/受保护的/公共类
和成员 (默认)
-p -private 显示所有类和成员
-c 对代码进行反汇编
-s 输出内部类型签名
-sysinfo 显示正在处理的类的
系统信息 (路径, 大小, 日期, MD5 散列)
-constants 显示静态最终常量
-classpath 指定查找用户类文件的位置
-bootclasspath 覆盖引导类文件的位置

22.throw和throws的区别?

throw

  1. 在方法体内抛出一个异常对象,是程序员自己new,并抛出
  2. 可以单独作为一条语句使用
  3. 如果异常是非RuntimeException,则需要throws或者try-catch
  4. 执行到throw之后就跳出该方法的执行(依旧可以执行finally语句块)

throws

  1. 使用在方法的声明上,必须位于参数列表之后,表示抛出一个或多个异常
  2. 需要有方法的调用者进行处理异常

23.常见的异常类有哪些?

异常有很多,Throwable是异常类的根类
Throwable包含两个子类,Error错误,Exception异常
Exception有分为一般异常和运行时异常RuntimeException(运行时异常不需要代码的显示捕获和处理)

常见的异常及其父子关系:

Throwable

Error

IOError
LinkageError
ReflectionError
ThreadDeath
VirtualMachineError

Exception

CloneNotSupportedException
DataFormatException
InterruptedException
IOException
ReflectiveOperationException
RuntimeException

ArithmeticException
ClassCastException
ConcurrentModificationException
IllegalArgumentException
IndexOutOfBoundsException
NoSuchElementException
NullPointerException
SecurityException

SQLException


24.什么是JAVA内部类?

  1. 概念:存在于java类内部的Java类
  2. 内部类的作用:
    匿名内部类提供了实例化对象或接口的快捷方式;
    内部类提供了“java多继承”的解决方案
  3. 特点:
    内部类仍然是一个独立的类,在编译后会生成两个字节码文件
    内部类不能用普通的方式访问。内部类是外部类的一个成员,因此内部类可以自由的访问外部类的成员变量,无论是否是private的
    静态内部类只能访问外部类的静态成员变量和方法
  4. 分类:

1.成员内部类

编译后会产生两个字节码文件(OuterClass.class、OuterClass$InnerClass.class)

class OuterClass {
    class InnerClass {} //成员内部类
}

2.方法内部类

编译后会产生两个字节码文件(OuterClass.class、OuterClass$1InnerClass.class)
只能在定义该内部类的方法内实例化
方法内部类,只能使用该方法内的final局部变量
当一个方法执行结束时,其栈结构被删除,局部变量称为历史。但该方法结束时,在方法内创建的内部类对象仍然可能存储与堆中

class OuterClass {
    public void doSomething(){
        class Inner{
        }
    }
}

3.匿名内部类

public class Fish {
    /**
     * 游泳方法
     */
    public void swim() {
        System.out.println("我在游泳!");
    }

    public static void main(String[] args) {
        //创建鱼对象
        Fish fish = new Fish() {	//创建的是Fish的子类,多态
            //重写swim方法
            public void swim() {
                System.out.println("我在游泳,突然发生海啸,我撤了!");
            }
        };
        
        fish.swim();
    }
}

编译后生成两个字节码文件:Fish.class、Fish$1.class

interface IFish {
    public void swim();
}

class TestIFish {
    
    public static void main(String[] args) {
        IFish fish = new IFish() {		//创建的是IFish接口的实现类
            @Override
            public void swim() {
                System.out.println("我是一条小丑鱼,我在游泳");
            }
        };
        
        fish.swim();
    }
}

编译后生成三个字节码文件:IFish.class、TestFish.class、TestFish$1.class

同样也可以将匿名内部类作为参数传递到方法内部

4.静态嵌套类

静态嵌套类只能访问类内部的静态成员变量和方法,在静态方法内部定义的内部类也是静态嵌套类,这时不能在类前面加static修饰

class OuterFish {
    static class InnerFish {
    }
}

class TestStaticFish {
    public static void main(String[] args) {
        //创建静态内部类对象
        OuterFish.InnerFish iFish = new OuterFish.InnerFish();
    }
}

25.nio中的Files类常用方法有哪些?

isExecutable:文件是否可以执行
isSameFile:是否同一个文件或目录
isReadable:是否可读
isDirectory:是否为目录
isHidden:是否隐藏
isWritable:是否可写
isRegularFile:是否为普通文件
getPosixFilePermissions:获取POSIX文件权限,windows系统调用此方法会报错
setPosixFilePermissions:设置POSIX文件权限
getOwner:获取文件所属人
setOwner:设置文件所属人
createFile:创建文件
newInputStream:打开新的输入流
newOutputStream:打开新的输出流
createDirectory:创建目录,当父目录不存在会报错
createDirectories:创建目录,当父目录不存在会自动创建
createTempFile:创建临时文件
newBufferedReader:打开或创建一个带缓存的字符输入流
probeContentType:探测文件的内容类型
list:目录中的文件、文件夹列表
find:查找文件
size:文件字节数
copy:文件复制
lines:读出文件中的所有行
move:移动文件位置
exists:文件是否存在
walk:遍历所有目录和文件
write:向一个文件写入字节
delete:删除文件
getFileStore:返回文件存储区
newByteChannel:打开或创建文件,返回一个字节通道来访问文件
readAllLines:从一个文件读取所有行字符串
setAttribute:设置文件属性的值
getAttribute:获取文件属性的值
newBufferedWriter:打开或创建一个带缓存的字符输出流
readAllBytes:从一个文件中读取所有字节
createTempDirectory:在特殊的目录中创建临时目录
deleteIfExists:如果文件存在删除文件
notExists:判断文件不存在
getLastModifiedTime:获取文件最后修改时间属性
setLastModifiedTime:更新文件最后修改时间属性
newDirectoryStream:打开目录,返回可迭代该目录下的目录流
walkFileTree:遍历文件树,可用来递归删除文件等操作


26.什么是反射?反射的作用?

  1. Reflection(反射)被视为动态语言的关键,反射机制允许程序在执行期间通过Reflection API获取任何类的内部信息,并能直接操作任意对象的内部属性和方法;
  2. 每个类加载完成之后,就会在内存中创建一个Class类型的对象(一个类只有一个Class对象),这个对象包含的当前类的所有结构信息,是对当前类的一个描述对象;
  3. 反射机制提供的功能(在运行时)

判断任意一个对象所属的类
创建任意一个对象
判断/调用任意一个对象所具有的成员变量和方法
获取泛型信息
处理注解
生成动态代理

  1. 反射的作用:
    反射能够降低代码的耦合性,反射的过度使用会严重消耗系统资源。

27.动态代理是什么?应用场景?

动态代理:在运行时,创建目标类,可以调用和扩展目标类的方法

Java中实现动态代理的方式:

  • JDK中的动态代理
  • Java类库的CGLib

应用场景:

  • 统计每个API的请求耗时
  • 统一的日志输出
  • 检验被调用的API是否已经登录和权限鉴定
  • Spring中的AOP功能模块就是采用动态代理机制来实现切面编程的

28.怎么实现动态代理

  • JDK 动态代理
  • CGLib 动态代理
  • 使用 Spring aop 模块完成动态代理功能

29.什么是java序列化?什么情况下需要序列化?

  1. ObjectOutputStream:内存中的对象 ====> 存储中的文件、通过网络传输出去 ====> 序列化过程
  2. ObjectInputStream:存储中的文件、通过网络传输出去 ====> 内存中的对象 ====> 序列化过程

对象序列化机制允许把内存中的java对象转成平台无关的二进制流,从而允许把这种二进制流持久化地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。当其他程序获取了这种二进制流,就可以恢复成原来的java对象;

重点:实现序列化的对象需要满足的要求

  1. 需要实现:Serializable或Externalizable
  2. 当前类需要提供一个全局常量:serialVersionUID
  3. 除了当前Person类需要实现Serializable接口之外,还必须保证其内部所有属性也必须是可序列化的。(默认情况下,基本数据类型可序列化)
  4. ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量

SerialVersionUID的理解
如果在将对象进行序列化时没有显示的声明serialVersionUID,那么在java会根据运行时的环境自动生成一个serialVersionUID;但是自动生成的serialVersionUID可能会出错(在对对象进行修改时,可能就找不到这个对象了,寻找的时候根据serialVersionUID)

具体使用

/*
ObjectOutputStream
 */
@Test
public void test1() throws IOException {
    ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ObjectStream.dat"));
    Person ayi1 = new Person("ayi1", 18);
    objectOutputStream.writeObject(ayi1);
    objectOutputStream.writeUTF(new String("123456"));
    objectOutputStream.writeObject(new Integer[]{1,2,3,4,5,6});

    objectOutputStream.close();
}

/*
ObjectInputStream
 */
@Test
public void test2() throws IOException, ClassNotFoundException {
    ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("ObjectStream.dat"));
    Person ayi1 = (Person) objectInputStream.readObject();
    String str = objectInputStream.readUTF();
    Integer[] arr = (Integer[]) objectInputStream.readObject();

    System.out.println("ayi1 = " + ayi1);
    System.out.println("str = " + str);
    for (int i = 0; i < arr.length; i++) {
        System.out.println("arr["+ i +"] = " + arr[i]);
    }

    objectInputStream.close();
}

30.什么场景要对象克隆?

  • 方法需要 return 引用类型,但又不希望自己持有引用类型的对象被修改。
  • 程序之间方法的调用时参数的传递。有些场景为了保证引用类型的参数不被其他方法修改,可以使用克隆后的值作为参数传递。

31.深拷贝和浅拷贝区别是什么?

浅拷贝:只复制基本数据类型的属性,引用数据类型依旧引用的是同一个对象(复制的是引用数据类型的地址值)
深拷贝:复制基本数据类型的属性,同时创建新的引用数据类型属性的对象,并拷贝其内部属性
在这里插入图片描述
在这里插入图片描述


32.如何实现对象克隆与深拷贝?

  1. 实现 Cloneable 接口,重写 clone() 方法。(只能完成浅拷贝)
  2. 不实现 Cloneable 接口,会报 CloneNotSupportedException 异常。
  3. Object 的 clone() 方法是浅拷贝,即如果类中属性有自定义引用类型,只拷贝引用,不拷贝引用指向的对象。
public class Person implements Cloneable{
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
	
	//省略getters and setters、toString
	
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

   @Test
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person1 = new Person("张三", 18);
        Person person2 = (Person) person1.clone();
        System.out.println("person2 = " + person2);
    }
}

可以使用下面的两种方法,完成 Person 对象的深拷贝。

方法1、对象的属性的Class 也实现 Cloneable 接口,在克隆对象时也手动克隆属性。

public class Person implements Cloneable{
    private String name;
    private int age;
    private Food food;

    public Person(String name, int age, Food food) {
        this.name = name;
        this.age = age;
        this.food = food;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Food getFood() {
        return food;
    }

    public void setFood(Food food) {
        this.food = food;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", food=" + food +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person person = (Person) super.clone();
        person.setFood((Food) person.getFood().clone());
        return person;
    }

    @Test
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person1 = new Person("张三", 18, new Food("apple", 20));
        Person person2 = (Person) person1.clone();
        person2.getFood().setName("pear");
        System.out.println("person1 = " + person1);
        System.out.println("person2 = " + person2);
    }
}

class Food implements Cloneable{
    private String name;
    private int money;

    public Food(String name, int money) {
        this.name = name;
        this.money = money;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Food{" +
                "name='" + name + '\'' +
                ", money=" + money +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

方法2、结合序列化(JDK java.io.Serializable 接口、JSON格式、XML格式等),完成深拷贝
结合 java.io.Serializable 接口,完成深拷贝

public Person cloneWithSerialize(){
        ByteArrayOutputStream baos = null;
        ObjectOutputStream outputStream = null;
        ByteArrayInputStream bais = null;
        ObjectInputStream inputStream = null;
        Person person = null;
        try {
            baos = new ByteArrayOutputStream();
            outputStream = new ObjectOutputStream(baos);

            outputStream.writeObject(this);

            bais = new ByteArrayInputStream(baos.toByteArray());
            inputStream = new ObjectInputStream(bais);
            person = (Person) inputStream.readObject();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bais != null) {
                try {
                    bais.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (baos != null) {
                try {
                    baos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return person;
    }

    @Test
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person1 = new Person("张三", 18, new Food("apple", 20));
        Person person2 = person1.cloneWithSerialize();
        person2.getFood().setName("pear");
        System.out.println("person1 = " + person1);
        System.out.println("person2 = " + person2);
    }

33.Java跨平台运行的原理

  1. .java 源文件要先编译成与操作系统无关的 .class 字节码文件,然后字节码文件再通过 Java 虚拟机解释成机器码运行。
  2. .class 字节码文件面向虚拟机,不面向任何具体操作系统。
  3. 不同平台的虚拟机是不同的,但它们给 JDK 提供了相同的接口。
  4. Java 的跨平台依赖于不同系统的 Java 虚拟机。

34.Java的安全性体现在哪里?

  1. 使用引用取代了指针,指针的功能强大,但是也容易造成错误,如数组越界问题。
  2. 拥有一套异常处理机制,使用关键字 throw、throws、try、catch、finally
  3. 强制类型转换需要符合一定规则
  4. 字节码传输使用了加密机制
  5. 运行环境提供保障机制:字节码校验器->类装载器->运行时内存布局->文件访问限制
  6. 不用程序员显示控制内存释放,JVM 有垃圾回收机制

35.Java针对不同的应用场景提供了哪些版本?

  1. J2SE:Standard Edition(标准版) ,包含 Java 语言的核心类。如IO、JDBC、工具类、网络编程相关类等。从JDK 5.0开始,改名为Java SE。
  2. J2EE:Enterprise Edition(企业版),包含 J2SE 中的类和企业级应用开发的类。如web相关的servlet类、JSP、xml生成与解析的类等。从JDK 5.0开始,改名为Java EE。
  3. J2ME:Micro Edition(微型版),包含 J2SE 中的部分类,新添加了一些专有类。一般用设备的嵌入式开发,如手机、机顶盒等。从JDK 5.0开始,改名为Java ME。

36.什么是JVM?

  1. Java Virtual Machine(Java虚拟机)的缩写
  2. 实现跨平台的最核心的部分
  3. .class 文件会在 JVM 上执行,JVM 会解释给操作系统执行
  4. 有自己的指令集,解释自己的指令集到 CPU 指令集和系统资源的调用
  5. JVM 只关注被编译的 .class 文件,不关心 .java 源文件

37.什么是JDK?

  1. Java Development Kit(Java 开发工具包)的缩写。用于 java 程序的开发,提供给程序员使用
  2. 使用 Java 语言编程都需要在计算机上安装一个 JDK
  3. JDK 的安装目录 5 个文件夹、一个 src 类库源码压缩包和一些说明文件
  • bin:各种命令工具, java 源码的编译器 javac、监控工具 jconsole、分析工具 jvisualvm 等
  • include:与 JVM 交互C语言用的头文件
  • lib:类库
  • jre:Java 运行环境
  • db:安装 Java DB 的路径

src.zip:Java 所有核心类库的源代码
jdk1.8 新加了 javafx-src.zip 文件,存放 JavaFX 脚本,JavaFX 是一种声明式、静态类型编程语言


38.什么是JRE?

  1. Java Runtime Environment(Java运行环境)的缩写
  2. 包含 JVM 标准实现及 Java 核心类库,这些是运行 Java 程序的必要组件
  3. 是 Java 程序的运行环境,并不是一个开发环境,没有包含任何开发工具(如编译器和调试器)

39.JDK、JRE、JVM之间的关系是什么样的?

  1. JDK 是 JAVA 程序开发时用的开发工具包,包含 Java 运行环境 JRE
  2. JDk、JRE 内部都包含 JAVA虚拟机 JVM
  3. JVM 包含 Java 应用程序的类的解释器和类加载器等

40.Java中基本类型的转换规则

  1. = 右边先自动转换成表达式中最高级的数据类型,再进行运算。整型经过运算会自动转化最低 int 级别,如两个 char 类型的相加,得到的是一个 int 类型的数值。
  2. = 左边数据类型级别 大于 右边数据类型级别,右边会自动升级
  3. = 左边数据类型级别 小于 右边数据类型级别,需要强制转换右边数据类型
  4. char 与 short,char 与 byte 之间需要强转,因为 char 是无符号类型

41.if-else与switch的区别

if-else-if-else:

  1. 适合分支较少
  2. 判断条件类型不单一
  3. 支持取 boolean 类型的所有运算
  4. 满足条件即停止对后续分支语句的执行

switch:

  1. 适合分支较多
  2. 判断条件类型单一,JDK1.0-1.4 数据类型接受 byte short int char; JDK1.5 数据类型接受 byte short int char enum; JDK1.7 数据类型接受 byte short int char enum String
  3. 没有 break 语句每个分支都会执行

42.Java中数组有什么特征?

  1. 在内存中申请一块连续的空间
  2. 数组下标从 0 开始
  3. 每个数组元素都有默认值,基本类型的默认值为 0、0.0、false,引用类型的默认值为 null
  4. 数组的类型只能是一个,且固定,在申明时确定
  5. 数组的长度一经确定,无法改变,即定长。要改变长度,只能重新申明一个

43.可变参数的作用和特点是什么?

作用:在不确定参数的个数时,可以使用可变参数

特点:

  1. 每个方法只能有一个可变参数,且只能是方法的最后一个参数
  2. 可变参数可以是任意类型:引用类型,基本类型
  3. 不能通过可变参数来对方法进行重载
  4. 通过对.class文件进行反编译可以发现,可变参数被处理成了数组

44.类和对象的关系

类是对象的抽象概念,对象是类的一个具体实体;
类是抽象概念,不会占用内存;对象是具体实例,会占用内存


45.说一说你对面向过程和面向对象的理解?

软件开发思想,先有面向过程,后有面向对象
在大型软件系统中,面向过程的做法不足,从而推出了面向对象
都是解决实际问题的思维方式
两者相辅相成,宏观上面向对象把握复杂事物的关系;微观上面向过程去处理
面向过程以实现功能的函数开发为主;面向对象要首先抽象出类、属性及其方法,然后通过实例化类、执行方法来完成功能
面向过程是封装的是功能;面向对象封装的是数据和功能
面向对象具有继承性和多态性;面向过程则没有


46.方法重载和重写是什么?有什么区别?

重写:在子类中,将父类的成员方法的返回值类型,方法体,访问权限的过程;
重写的一些规则:

  1. 参数列表必须和父类的参数列表一样
  2. 子类重写父类的方法时,访问权限必须比父类的要大或等于
  3. 子类重写父类的方法时,返回值类型必须是父类的子类或等于
  4. 子类重写父类的方法时,抛出的异常必须是父类的子类或等于(不能抛出新的异常)
  5. static、final、private的方法不能被重写

重载:同一个类中运行存在一个或多个同名方法,他们的参数个数或类型不同
重载的条件:方法名相同;参数的个数、类型、顺序不同
重载的一些规则:

  1. 被重载的方法的参数列表不同
  2. 被重载的方法可以修改返回值类型(重写只能是父类返回值类型的子类)
  3. 被重载的方法可以修改访问权限
  4. 被重载的方法可以修改抛出的异常
  5. 方法可以在同一个类中,或其子类中被重载
  6. 无法以返回值类型作为被重载方法的区分标准

重载和重写的区别?

  1. 作用范围:重写发生在父类继承子类时,重载发生在同一类中
  2. 参数列表:重写的参数列表必须相同,重载的参数列表必须不同
  3. 返回值类型:重写的方法的返回值必须的父类方法的返回值的子类或相等;重载的方法的返回值可以自己重新设置
  4. 访问权限:重写的方法的访问权限必须比父类方法的访问权限大或等于;重载的方法的访问权限可以自己重新设置
  5. 抛出的异常:重写的方法的访问权限必须是父类抛出异常的子类,不能新增抛出的异常;重载可以随机设置

47.this和super关键字的作用

this:

  1. 对象内部指代自身的引用
  2. 解决成员变量和局部变量同名问题
  3. 可以调用成员变量,不能调用局部变量
  4. 可以调用成员方法
  5. 在静态方法当中不允许出现 this 关键字

super:

  1. 代表对当前对象的直接父类对象的引用
  2. 可以调用父类的非 private 成员变量和方法
  3. super(); 可以调用父类的构造方法,只限构造方法中使用,且必须是第一条语句

48.java.lang.Object的常用方法?

public final native Class<?> getClass(); 获取类结构信息
public native int hashCode() 获取哈希码
public boolean equals(Object) 默认比较对象的地址值是否相等,子类可以重写比较规则
protected native Object clone() throws CloneNotSupportedException 用于对象克隆
public String toString() 把对象转变成字符串
public final native void notify() 多线程中唤醒功能
public final native void notifyAll() 多线程中唤醒所有等待线程的功能
public final void wait() throws InterruptedException 让持有对象锁的线程进入等待
public final native void wait(long timeout) throws InterruptedException 让持有对象锁的线程进入等待,设置超时毫秒数时间
public final void wait(long timeout, int nanos) throws InterruptedException 让持有对象锁的线程进入等待,设置超时纳秒数时间
protected void finalize() throws Throwable 垃圾回收前执行的方法


49.子类构造方法的执行过程是什么样的?

1.如果子类构造方法中没有显示调用super(…)父类的构造方法,或者没有调用子类的this(…)构造方法,系统就会默认调用父类的无参构造方法;
2.如果存在多级继承关系,在创建一个对象时,以上的规则会自动向更高一级的父类应用,直到执行父类的无参构造方法为止;


50.什么是Java的多态?

实现多态的条件:

  1. 继承的存在。继承是多态的基础,没有基础就没有多态;
  2. 子类一旦重载父类中的方法,JVM就会调用子类中已经重写的方法,否则就会默认调用父类中继承的方法;
  3. 父类引用指向子类对象;

向上转型:父类引用指向子类对象会自动类型转换

  1. 父类引用变量调用的方法是子类重写父类或继承父类的方法,而不是父类中的方法;
  2. 父类引用变量,不能调用子类中扩展的方法(因为父类引用变量,实际上是一个父类,但父类中的属性和方法,都是子类中覆盖父类 )

向下转型:将一个指向子类对象的父类引用,赋值给一个子类引用,需要强制类型转换

  1. 向下转型必须转换为父类引用指向的子类对象的引用;
  2. 向下转型时可以结合instanceof进行判断

程序分析题

1.在方法中形参中String和StringBuilder,对实参的影响

public static void main(String[] args) {
        String str = "abc";
        StringBuilder stringBuilder = new StringBuilder("abc");
        System.out.println("str = " + str);
        System.out.println("stringBuilder = " + stringBuilder);

        System.out.println("------------------------------------");
        changeValue(str, stringBuilder);
        System.out.println("str = " + str);
        System.out.println("stringBuilder = " + stringBuilder);
    }


    public static void changeValue(String str, StringBuilder stringBuilder){
        str = "changedString";
        stringBuilder.delete(0, stringBuilder.length());
        stringBuilder.append("changeStringBuilder");
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值