JAVA面试库

1、基础

1.1、面向对象编程有哪些特性

1、抽象

抽象就是对同一个目标的共有的属性、特征、方法、功能、行为等进行抽取并归纳总结,它是一种将复杂现实简单化为模型的过程,它关注的是对象行为,而不用关注具体的实现细节。

在面向对象编程中,抽象主要是通过抽象类和接口来实现的:

  • 抽象类是不能被实例化的,它包含一些抽象的方法和具体的实现方法。
  • 接口是一种特殊的抽象类型,一般仅包含抽象方法的声明,在java8+中可以包含默认的方法和静态方法实现

抽象可以在不知道具体实现的情况下编程,提高了代码的灵活性和拓展性。

2、封装

封装就是指隐藏对象的属性和实现细节,将对象的数据、属性、行为、方法等组合到下一个单一的单元中,并通过访问修饰符控制成员属性的访问和修改权限,再通过特定公开的方法setter和getter方法暴露给外面访问。

private String name = "ccshen";

public String getName(){
    return name;
}

name属性被private封装起来,外面只能通过对象的getName才能访问。

3、继承

继承是一种实现代码重用的机制,允许一个类继承另一个类的成员和方法,使得子类也能具有父类相同的行为。

继承是通过 extends 关键字实现的:

public class Dog extends Animal{
    @Override
    void eat(){
        System.out.printIn("狗吃饭")
    }
}

java类之间只能实现单继承,接口之间可以多继承。

4、多态 

多态指同一个行为在不同情况下的多种不同表现形式或形态,主要体现为同一个接口或父类的引用指向不同的实现对象,并能够在运行时动态决定调用的具体实现,这使得程序具有更好的灵活性和可扩展性。

Animal animal = new Dog();
animal.eat();

通过父类引用变量指向子类时,当调用父类的方法时,它实际上会根据实际对象的类型,去调用子类中的方法。

1.2、JDK与JRE的区别

JDK:是整个Java的核心,包含了Java的运行环境(JRE)和一系列Java开发工具完整包。

JRE:是Java程序的运行环境,包含JVM、Java核心类库等。JRE只能运行Java应用程序,不能用于编译开发,它是JDK的子集。

安装JDK后就有JRE目录,JRE属于JDK的子集。

1.3、如何编译和运行Java文件  

使用javac命令来编译.java文件        

javac Test.java

运行后会生成Test.class文件

1.4、Java标识符命名规则

  • 标识符只能由字母、数字、下划线(_)、美元符号($)组成
  • 标识符不能由数字开头
  • 标识符不能使用Java关键字,比如 for 
// 类名
class User(){
    
    // 变量名
    int firstName;
    // 常量名
    private static final int default_code = 60;

    // 合法方法名
    public void setFirstName(int firstName){
        this.firstName = firstName;
    }
}

1.5、Java定义常量

Java常量是指使用final修饰的变量,它们的值在初始化后不能被改变

final int age = 30;

加修饰范围,以及静态关键字

private static final age = 30;

常量的作用域可以是类级别(普通、静态),方法级别。

1.6、Java有哪几种基本数据类型 

Java有 4类 8种 数据类型

整数型:byte、short、int、long

浮点型:float、double

字符型:char

布尔型:boolean

1.7、== 和 equals 比较有什么区别

==

如果比较的对象是基本的数据类型,则比较的是数值是否相同

如果比较的对象是引用数据类型,则比较的是对象的地址是否相同

equals

equals是Object类提供的方法,接收Object参数类型

用来比较两个对象是否相等,默认比较的是对象地址,不能用于比较基本数据类型,但可以是包装类型,所以,如果是要比较两个对象的值是否相等,一般需要重写equals与hashcode方法 。

比如常用的String、Date、Integer等类都重写了equals和hashcode方法,比较的是存储对象的内容是否相等,而不是堆内存地址。     

1.8、s1 = s1 + 1 和 s1 += 1 区别 

如果s1原有数据类型小于int类型,则s1 = s1 + 1发生编译异常

因为s1是short类型,1是int类型,所以计算结果为int类型,它不能自动转换为比它小的数据类型,所以发生编译异常。

解决办法使用类型强制转换:

public static void main(String[] args){
    short s1 = 1;
    s1 = (short)(s1 + 1);
    // s1 = 2
}

使用s1 += 1不会有问题,因为 s1 += 1;支持隐式强制类型转换

public static void main(String[] args){
    short s1 = 1;
    s1 += 1;
    // s1 = 2
}

1.9、float n = 1.8 有错吗

有错!!!

因为double是双精度型浮点型,float是单精度型浮点型。

数字1.8默认是double类型,如果将一个double类型的值赋值给一个float类型的变量时,需要进行类型转换,因为double类型的精度高于float类型。

这种情况,如果不显示进行类型转换,编译器报错,因为可能会有精度丢失

因此需要强制类型转换或后面加上F:

float n = (float)1.8;

float = 1.8F;

1.10、i++ 与 ++i 区别

i++是先赋值后加1,++1是先加1后赋值。也正是这种顺序的差异,++i能够当作左值来使用,i++却不可!

a=i++,先将i的值赋给a,然后再进行i=i+1的操,最后把加+1后的结果再赋值给i。

b=++i,先进行i=i+1操作,然后把加1后的结果赋值给i,最后把i的值赋给b。

无论是i++还是++i,i自己都是加了1。只不过运算存在顺序不同,所以把他整体赋值给变量时会不同。

public static void main(String[] args) throws Exception{
    int i = 1;
    int a = i++;
    int j = 1;
    int b = ++j;
    // a = 1 , i = 2
    // b = 2 , j = 2
    
}

1.11、while 与 do while 区别

while与do while都是循环语句

  • while 是先判断条件再执行循环
  • do whil e是先执行循环再判断条件
public static void main(String[] args) throws Exception{
    // while
    int i = 0;
    while(i < 3){
        i++;
        System.out.printIn(i);

    }

    // do while
    int j = 0;
    do{
        j++;
        System.out.printIn(j);

    } while(i < 3);
    
}

同样的条件下,如果初始条件不成立,do while会多执行一次。

1.12、如何跳出java循环

关键字说明
continue跳出当前本次循环
break跳出整个循环
return跳出整个循环及当前方法


public int getData(){
   for(int i = 0; i < 10; i++){
        if(i == 1){
            // 跳出本次循环,继续下次循环
            continue;
        }
        
        if(i == 5){
            // 跳出整个循环,走下面代码
            break;
        }
   }
   // 结束方法,返回结果
   return i;
}
    

 1.13、如何跳出java多层嵌套循环

  • 标号方式

在外面的循环语句前定义一个标号,然后在里层循环里面定义带有标号的break语句。

public static void main (String[] args){
    tag:
    for(int i = 0; i < 10; i++) {
        for(int j = 0; j < 10; j++) {
            if(j == 5) {
                break tag;
            }
        }
    }
}

  • 设置标志位
public static void main (String[] args){
    boolean flag = false;
    for(int i = 0; i < 10; i++) {
        for(int j = 0; j < 10; j++) {
            if(i == 0 && j == 5) {
                flag = true;
                break;
            }
        }
        if(flag){
            break;
        }
    }
}

  • 内层循环抛出异常
public static void main (String[] args){
    for(int i = 0; i < 10; i++) {
        for(int j = 0; j < 10; j++) {
            if(i == 1 && j == 5) {
              throw new Exception();
            }
        }
    }
}

1.14、& 与 && 区别 

  • &     位运算符;逻辑运算符

按逻辑运算符时,&左右两边有一个条件为假就会不成立,但是2端都会运行,比如(1+2)== 4 &(1+2)== 3;即使1+2 == 4为假也会去判断1+2 == 3是否成立。

  • &&  逻辑运算符

&& 也叫短路运算符,因为只要左端条件为假不成立,不会去判断右端的条件。

相同点:只要有一端为假,则语句不成立

1.15、java数组初始化

  • 静态初始化:创建数组时指定数组元素
int[] unmbers = [1,2,3,4,5];
String[] strs = ["a","b","c"];
  • 动态初始化:先指定数组长度,然后逐个赋值
int[] numbers = new int[5];
numbers[0] = 1;
numbers[1] = 2;
numbers[2] = 3;
numbers[3] = 4;
numbers[4] = 5;
  • 多维数组初始化
int[][] numbers = {
    {1,2,3},
    {4,5,6},
    {7,8,9}
};

1.16、数组有length属性,String有length()方法

public static void main(String[] args){

    String[] names = {"ccshen","Li","Le"};
    String str = "ccshen";
    System.out.printIn(names.length);
    System.out.printIn(str.length());

}

1.17、java种的值传递与引用传递区别分析

java中数据类型分为两类:基本数据类型和对象引用类型。

基本数据类型:包括  byteshort,int,long,float,doublecharboolean,它们都在栈中存储数据

对象引用类型:包括类、接口和数组等,它们在栈中存储引用,在堆中存储实例

  • 值传递:方法调用时,实参是基本数据类型,形参是实参的拷贝,形参的改变不影响实参。
public void swap(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
}
 
int x = 10;
int y = 20;
swap(x, y); // 调用后,x 和 y 的值不变,因为传递的是它们的值的拷贝
  • 引用传递:方法调用时,实参是对向引用类型,形参与实参指向同一个对象。
public void swap(Person p1, Person p2) {
    Person temp = p1;
    p1 = p2;
    p2 = temp;
}
 
Person x = new Person("Alice");
Person y = new Person("Bob");
swap(x, y); // 调用后,x 和 y 的引用可能会交换,但它们引用的对象内容不会改变

在Java中,虽然我们通常说是传值或传引用,但实际上,参数传递统一使用的是值传递。对于对象引用类型,实际上传递的是对象的引用值,这个值是对象的内存地址。因此,方法参数传递时,基本数据类型按值传递,对象引用按值传递(传递的是引用的副本),但是传递的是引用的地址值,这个地址值是可以操作的,如果需要修改原对象,可以通过返回值或将对象作为引用传递给方法内部进行操作。

Java只有值传递,没有引用传递!-CSDN博客

1.18、java中的构造方法是什么

构造方法是构造类的主要方法,java中每个类都必须要有构造方法,构造方法名与类名相同,没有返回类型,new 一个对象的时候就会调用指定的构造方法。

一个类至少要有一个构造方法,可以有多个构造方法,即构造方法重载,方法参数的数量不同或类型不同。

如果没有显示的创建构造方法,java编译器也会为类提供一个默认的无参构造方法。

@Data
public class User(){
    private String name;
    private int age;
    // 构造方法
    public User(String name, int age){
        this.name = name;
        this.age = age;
    }
    // 重载
    public User(String name){
        this.name = name;
    }
}

public void Test(){
    // 构造函数创建对象
    User user = new User("ccshen",30);
}

1.19、static关键字

1、static关键字有什么用

static 关键字在 Java 中的作用是为类创建一个静态成员。被 static 修饰的成员属于类本身,而不是类的某个特定实例。这意味着你可以不创建类的对象就直接访问静态成员。static 关键字可以用来修饰内部类、方法、变量、代码块。

  • 静态内部类:静态内部类可以不依赖外部类实例对象而被实例化,而内部类需要在外部类实例化后才能被实例化。
  • 静态方法:静态方法属于类方法,不需要实例化对象就能调用
  • 静态变量:静态变量属于类,不需要实例化对象就能调用
  • 静态代码块:静态代码块只会在类被加载时执行且执行一次
public class Test(){
    // 静态代码块
    static{
        
    }

    // 静态内部类
    // @Data
    static class User(){
        String name;
        int age;
        Score score;
    }

    // @Data
    static class Score(){
        String chinese;
        String english;
    }

    // 静态变量 Test.id直接访问变量,不需要创建类
    public static int id = 1;

    // 静态方法 Test.getData直接调用方法,不需要创建Test对象再调用
    public static void getData(){
    
    }
}

2、static变量与普通变量的区别

  • 所属目标不同:静态变量属于类的变量,普通变量属于对象的变量
  • 储存区域不同:静态变量储存在方法区的静态区,普通变量储存在堆区。jdk1.7及以上,静态变量储存在其对应的class对象中,而class对象与其他普通的class对象一样,都储存在堆中。
  • 加载时间不同:静态变量随着类的加载而加载,随着类的消失而消失;普通变量随着对象的加载而加载,随着对象的消失而消失。
  • 调用方式不同:静态变量只能通过类名、对象调用,普通变量只能通过对象调用。

3、static不可以修饰局部变量 ,修饰的目标是:内部类、全局的成员变量、方法、代码块。

4、static 方法中不可以使用this或super,否则会导致编译错误

因为this或super是实例化对象后的操作,而static属于类级别,无法指向任何实例。 

1.20、final关键字 

修饰类型说明
修饰类表示该类不能被继承
修饰方法表示该方法不能被重写
修饰变量表示常量,只能赋值一次,不能被修改

1.21、final、finally、finalize有什么区别

final:

  • 如果修饰类,该类不能被继承
  • 如果修饰方法,该方法不能被重写
  • 如果修饰变量,该变量是常量,不能被修改

finally:

finally是try-catch-finally最后一部分,表示发生任何情况都会执行,finally部分可以省略,但如果finally部分存在,则一定会执行finally里面的代码(发生ERROR错误等非程序异常除外)

finalize

finalize是Object的一个方法,在垃圾收集器执行的时候会自动被调用被回收对象的此方法,一般不建议主动使用

1.22、java支持多继承吗

  • 类与类之间不支持多继承,只支持单继承

  • 接口与接口之间支持多继承

1.23、java类可以实现多个接口 

JDK concurrentHashMap集合类源码,可以看到继承一个抽象类,实现2个接口。

1.24、重写与重载

  • 区别

重写是子类可以根据自身的特征,重新实现父类方法的业务逻辑,但是方法名称,参数列表,返回值必须与父类保持一致;(子类通过继承实现父类方法,方法名称,参数列表,返回值不变)

重载是一个类中允许定义多个方法名相同,返回值可以相同可以不同,但是参数列表必须不同的方法;(构造方法)

重写是子类与父类间多态性的表现,重载是一个类中方法间多态性的表现。

  • 构造器可以被重载,不可以被重写

一个类的构造器只属于当前类,它不能被继承,所以构造器不能被重写;

一个类里面可以有多个构造器,所以构造器可以被重载;

  • 私有方法能被重载,不可以被重写

因为private修饰方法只能在当前类可见,子类都见不到,不可能被重写,重写必须在protectd以上的作用域

  • 静态方法可以重载,不可以被重写

重载是一个类中可以有多个方法名相同,参数列表不同,与静态没有关系

重写是面向对象多态性的一种体现,需要有继承和子类对象的动态绑定,但是静态方法属于类的,不属于任何实例,不参与类动态绑定过程。

1.25、java异常有哪些分类

  • Throwable

Throwable是java异常的顶级类,所有异常都继承这个类。

Error和Exception是异常类的两个大分类。

  • Error

Error是非程序异常,即程序不能捕获的异常,一般是编译或系统性错误,如OutOfMemorry内存溢出异常等。

  • Exception

Exception是程序异常类,有程序内部产生,Exception又分为运行时异常、非运行时异常。

  • 运行时异常

运行时异常是程序运行才可能发生的异常,编译器编译不会检查,方法可以不用主动catch,也可以不用throws声明抛出运行时异常。

常见的运行时异常:NullPointException、IndexOutOfBoundsException、ClassCastException、ArithmeticException、SecurityException等

在程序中可以不用声明,也不用捕获。 

  • 非运行时异常

非运行时异常是编译器编译就会检查的异常,方法需要主动catch或throws声明受检查的异常,不然会出现编译错误。如常见的:IOException、ClassNotFoundException等。

在程序中必须声明或捕获。 

1.26、java中常见的异常有哪些

  • NullPointExcption

空指针异常,操作一个null对象的方法和属性抛出的异常。

  • OutOfMemoryError

内存溢出异常,这不是程序能控制的,是指要分配对象的内存超出当前最大的堆内存,需要调整堆内存大小(-Xmx)或优化程序

  • IOException

IO是input、output,我们在读写磁盘文件、网络内容的时候经常出现的一种异常,这种异常是受检查异常,需要进行手工捕获。

  • FileNotFoundException

文件找不到或文件不存在就会抛出这样的异常,如定义文件输入输出流,文件不存在会报错。

FileNotFoundException是IOException的子类,同样是受检查的,需要进行手工捕获。

  • ClassNotFoundException

类找不到异常,加载类找不到抛出异常,即类路径下不能加载指定类。它是受检查的异常,需要人工捕获。

  • ClassCastException 

类转换异常,将一个不是该类的实例转换成这个类就会抛出这个异常。这是运行时异常,不需要手工捕获。如数字强制转换为字符串就会抛出这样的异常。

  • NoSuchMethodException

没有这个方法异常,一般发生在发射调用方法的时候。它是受检查的异常,需要人工捕获。

  • IndexOutOfException 

索引越界异常,当操作一个字符串或数组时抛出的异常。运行时异常,不需要手工捕获。

  • ArithmetricException 

算术异常,数字算术运算时异常 ,如一个数字除以0就会报这个错。

运行时异常,可以手动捕获抛出异常

  • SQLException 

SQL异常,发生在操作数据库时的异常,它是受检查的异常,需要人工捕获。

1.27、java中避免空指针常见的方法 

字符串比较时常量放前面

String s = "ccshen";
if("shen".equals(s)){
    
}

初始化默认值

List<String> list = new ArrayList();
String str = "";

返回空集合

public List<User> getUsers(){
    List<User> userList = userDao.queryUserInfo();
    return (userList = null) ? new ArrayList() : userList;
}

 断言

断言是用来检查程序的安全性的,在使用之前就进行检查条件,如果不符合条件就报异常,符合就继续。Java中自带的断言关键字为:assert,如:

assert name == null : "名称不能为空";

 不过默认是不启动断言检查的,需要带上JVM参数:-anableassertions 才能生效。不过不建议使用,建议使用 Spring 中的,更强大,更方便好用。

Spring中的用法:

Assert.notNull(name,"名字不能为空");

Optionnal

Optional是java8新特性

String newName = Optional.ofNullable(name).else("");

1.28、throw与throws区别 

throw用在方法中,用来主动抛出一个异常;

throws用在方法声明中,声明方法可能会抛出异常;

2个不一定要同时用,如果方法中抛出的是RuntimeException及其子异常,则方法可以不用声明throws,否则必须要声明throws。

public void test1(){
    throw new RuntimeException("运行时异常");
}

public void test1() throws Exception{
    throw new Exception("发生异常");
}

1.29、try-catch-finally

try-catch-finally中catch与finally都可省略,但是不能同时省略,有try时后面必须有catch或finally

JDK7后可以一次catch多个异常

使用try-catch会影响性能吗

 一般情况下,try-catch执行时间很短,不会对性能产生显著的影响,但是在极端情况下,try-catch的使用可能会对性能有影响,频繁捕获抛出异常,尤其在高并发下,try-catch性能会成为瓶颈。

 

  • 22
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值