java基础知识及重要面试点-第二篇

目录

方法调用流程内存解析

成员变量和局部变量的区别

权限修饰符的作用范围

二维数组的动态初始化面试题

对象的内存机制

子类继承父类构造器的特点

怎样调用父类构造器

方法重写的使用注意事项

final修饰符

静态成员变量内存图

多态特点

如何解决无法调用子类特有的方法?强制类型转换

匿名内部类和Lambda表达式

匿名内部类

Lambda表达式

lambda省略格式

BigDecimal类

常用方法

Object类

Objects

StringJonier

包装类

Integer源码片段

Integer面试题

栈、堆、方法区、本地方法栈、寄存器

方法区:字节码文件加载时进入的内存

栈:方法运行时所进入的内存,变量也在这里

堆:new出来的东西会在这块内存中开辟空间并产生地址

方法调用流程内存解析

在一次程序执行的过程中,创建了多个对象,该类的字节码文件加载一次。每new一次,都会在堆内存中开辟一个独立的空间。成员方法的引用地址指向同一片内存空间。

成员变量和局部变量的区别

  • 编写位置不同

    • 成员变量 : 方法外

    • 局部变量 : 方法中

  • 初始化值不同

    • 成员变量 : 有默认初始化值

    • 局部变量 : 没有默认初始化值, 使用之前必须完成赋值.

  • 内存位置不同

    • 成员变量 : 堆内存

    • 局部变量 : 栈内存 -- 栈内存的所属方法当中

  • 生命周期不同

    • 成员变量 : 随着对象的创建而存在, 随着对象的消失而消失

    • 局部变量 : 随着方法的调用而存在, 随着方法的调用结束而消失.

  • 作用域

    • 都在自己所在的大括号中.

权限修饰符的作用范围

  • private : 在同一个类中

  • 默认的 : 在同一个类中, 在同一个包下进行访问

  • protected : 在同一类中,在同一包中,在不同包中被protected修饰类的子类

  • public : 可以在任意位置访问

二维数组的动态初始化面试题

int[][] arr=new int[2][3];
int[] arr1={11,22,33};
int[] arr2={44,55,66,77};
arr[0] =arr1;
arr[1]=arr2;

以上内容不会报错,原因在于:二维数组保存的是多个一维数组的地址值,arr1赋给arr[0]其实是arr1的地址赋给了arr[0].具体内存图如下:

对象的内存机制

子类继承父类构造器的特点

子类中所有的构造器默认都会先访问父类中的无参构造器,再执行自己。原因是子类在初始化时,有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据

怎样调用父类构造器

子类构造器的第一行语句默认是:super(),不写也存在

方法重写的使用注意事项

1、private方法不能被重写

2、static方法不能被重写

3、子类重写方法权限必须大于或等于父类中方法的权限

final修饰符

修饰该方法时,方法不能被重写,修饰类时,不能被继承,修饰变量时,该变量成为一个常量,不能被修改。

注:final修饰变量时,如果变量时基本数据类型,值不能被修改,如果是引用数据类型,内容可以修改,地址不能修改

例如

final int[] arr = {11,22,33};
        arr[0] = 100;
        System.out.println(arr[0]);

static修饰符的特点

1、随着类的加载而加载,类的加载时机是字节码文件运行时进入方法区的过程,优先于对象存在。

2、被同一个类多个对象共享

静态成员变量内存图

main方法进栈后,在执行第一句代码时,会将User的字节码文件加载到方法区,然后为User的静态变量划分一个专属于User静态成员的变量区。

多态特点

多态:在继承/实现关系中,不同子类对象展现各自的行为,这种现象叫多态

前提:1、要有继承

2、要有方法的重写

3、父类的引用指向子类的对象

注:成员变量没有多态,弊端:不能调用子类特有的方法。

如何解决无法调用子类特有的方法?强制类型转换

自动类型提升

父类名 对象名=new 子类名();//小的赋给大的

Person p=new Student();

强制类型转换

子类名 对象=(子类名)父类型变量;

Student s=(Student)p;

问题:当遇到传入的参数与强制类型转换需要的参数不一致时,会出现类型转换异常ClassCastException

原因:父类型变量记录的真实类型和要转换的类型不一致。

解决:判断真实类型 instanceof p instanceof Student

ElectricAlarmDoor实现了AlarmDoor接口中的抽象方法
main{
showElectricDoor(new ElectricAlarmDoor());
}

public static  void showElectricDoor(AlarmDoor alarmDoor){
        alarmDoor.BaoJing();
    }

匿名内部类和Lambda表达式

内部类分类:

  • 成员内部类(了解)
  • 静态内部类(了解)
  • 局部内部类(了解)
  • 匿名内部类(重点)

成员内部类格式:外部类名.内部类名 对象名 = new 外部类名().new 内部类名();

静态内部类格式:static修饰的成员内部类 外部类名.内部类名 对象名=new 外部类名.内部类名();

局部内部类格式:只能在方法中创建类,只能在方法中创建对象。

匿名内部类

前提:要有一个接口或类

new 类名或者接口名(){
    // 重写方法
}
举例:
    public interface Inter {
    void show();
}
new Inter() {
    @Override
    public void show() {
        System.out.println("匿名内部类");
    }
};

匿名内部类的本质:是一个继承了该类的子类【对象】,或者是一个实现了该接口的实现类【对象

使用场景

通常作为方法的参数传递

Thread t=new Thread()//构造器中参数有个能传入Runable接口,但接口不能实例化,所以需要使用匿名内部类
Thread t=new Thread(new Runable(){
public void run(){

}
})

Lambda表达式

前提:有一个接口,且接口中只有一个抽象方法

基本格式:

(形式参数) -> {代码}

(形式参数):  和接口中抽象方法中的参数保持一致
举例:1、无参数无返回值
public interface Addable {
    int add(int x,int y);
}
public class AddableDemo {
    public static void main(String[] args) {
       
        useAddable(new Addable() {
            @Override
            public void add(int x,int y) {
                return x + y;
            }
        });
        
        useAddable((int x,int y) -> {
            return x + y;
        });

    }

    public static void useAddable(Addable a) {
        int sum = a.add(10, 20);
        System.out.println(sum);
    }
}
lambda省略格式
  • 省略规则

    • 基本格式: (参数) -> {}

  • 参数

    • 没有参数: 小括号不能省略

      • () -> {System.out.println("黑马程序员");}

    • 有一个参数: 小括号和数据类型都可以省略

      • (String s) -> {System.out.println(s);}

      • s -> {System.out.println(s);}

    • 多个参数: 数据类型可以省略,参数类型要么都省略要么都留着

      • (int a,int b) -> {return a + b;}

      • (a,b) -> {return a + b;}

  • 语句体{}

    • 如果{} 中只有一条语句,可以省略大括号,分号,return

      • (int a,int b) -> {return a + b;}

      • (a,b) -> a + b

BigDecimal类

传统的double运算会出现运算结果不准确的结果

System.out.println(0.1+0.2);

方式一:
		 BigDecimal b1 = new BigDecimal("0.55555");
         BigDecimal b2 = new BigDecimal("0.54353455");
		 b1.add(b2)
 方式二:
             调用BigDecimal提供的静态方法valueof
              BigDecimal b3 = BigDecimal.valueOf(1.0234739749823);
        	  BigDecimal b4 = BigDecimal.valueOf(1.0234738749823);
			  b3.add(b4)

常用方法

public BigDecimal add(另一个BigDecimal对象)加法
public BigDecimal subtract (另一个BigDecimal对象)减法
public BigDecimal divide (另一个BigDecimal对象)除法
public BigDecimal multiply (另一个BigDecimal对象)乘法
public BigDecimal divide (另一个BigDecimal对象,精确几位,舍入模式)除法(如果除不尽会报错)参数二: 精确到小数点后几位参数三: 舍入模式向上取整: RoundingMode.UP向下取整: RoundingMode.DOWN四舍五入: RoundingMode.HALF_UP
public double doubleValue()返回double类型结果

当进行除法时会出现报错的问题

System.out.println(b3.add(b4));

解决办法

b3.divide(b4,2, RoundingMode.HALF_DOWN)

举例

  • 需求: double[] arr = {0.1,0.2,0.3}; 求和

public class Demo {
    public static void main(String[] args) {
        double[] arr = {0.1,0.2,0.3};
        // 求和
        // 1.定义求和变量
        BigDecimal sum = BigDecimal.ZERO;
        // 2.遍历数组
        for (int i = 0; i < arr.length; i++) { // i 0 1 2
            // 累加求和
            // 第1轮   0 和 0.1   结果赋值给了sum(0.1)
            // 第2轮   0.1 和 0.2 结果赋值给了sum(0.3)
            // 第3轮   0.3 和 0.3 结果赋值给了sum(0.6)
            sum = sum.add(BigDecimal.valueOf(arr[i]));
        }
        System.out.println(sum);
    }
}

Object类

  • 所有类都直接或者间接的继承自Object类

为什么打印对象名输出的是地址值?

println()底层源码会调用String类的valueof静态方法,valueof静态方法返回三目运算符的boolean值,会调用父类的toString方法,toString方法返回全类名和地址值。如果不想输出地址值可以重写toString方法。

同样的Object的equals方法底层也是比较的地址值,想要比较内容需要重写equals方法。

Objects

  • Objects工具类,提供了操作对象的方法

方法名说明
public static Boolean isNull(对象)判断对象是否为空
public static Boolean nonNull(对象)判断对象是否不为空
public static boolean equals(对象1,对象2)比较两个对象是否相同,避免空指针异常

注:Objects的equals方法可以避免空指针异常,底层做了判断,如果为空直接返回。

StringJonier

  • JDK8出现的API,可变的、操作字符串容器

  • 拼接字符串的时候,不仅高效,而且代码的编写更加的方便。

构造方法说明
public StringJoiner (间隔符号)创建一个StringJoiner对象,指定拼接时的间隔符号
public StringJoiner (间隔符号,开始符号,结束符号)创建一个StringJoiner对象,指定拼接时的间隔符号、开始符号、结束符号

方法名说明
public StringJoiner add (添加的内容)添加数据,并返回对象本身参数可以是String、StringBuilder等类型
public int length()返回长度 ( 字符的个数)
public String toString()返回一个字符串(该字符串就是拼接之后的结果)

举例:

StringJoiner stringJoiner = new StringJoiner("-");
String s="123456789";
char[] chars = s.toCharArray();
for (int i = 0; i < chars.length; i++) {
    stringJoiner.add(chars[i]+"");
}
System.out.println(stringJoiner);

包装类

自动装箱与自动拆箱

  • 自动装箱: 基本数据类型可以自动转换为包装类型。

  • 自动拆箱: 包装类型可以自动转换为基本数据类型。

自动装箱与自动拆箱原理

  • 自动装箱: 底层自动调用了Integer.valueOf方法

  • 自动拆箱: 底层自动调用了intValue方法

基本数据类型-> 字符串数据类型

public static String toString(基本数据类型)
public String toString()

字符串类型转换成数值本身对应的数据类型

public static int parseInt(String s)
public static Integer valueOf(String s)

Integer源码片段

public final class Integer{
    private final int value;
    
    public Integer(int value) {
    // 装箱,给成员变量赋值
        this.value = value;
    }
    
    public int intValue() {
    // 拆箱,获取成员变量的值
        return value;
    }
    
    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
}

Integer面试题

        

public class Demo {
    public static void main(String[] args) {
        Integer i1 = 100;
        Integer i2 = 100;
        System.out.println(i1 == i2); // true
        System.out.println("===========");
        Integer i3 = 200;
        Integer i4 = 200;
        System.out.println(i3 == i4); // false
    }
}

总结:数值范围不同所返回的结果也不同,自动装箱,底层自动调用valueOf方法,数据在-128~127之间直接使用缓存(已经存在的数组)的数据,数据不在-128到127之间:创建新的Integer对象

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值