当前位置: 首页 > article >正文

面渣逆袭之Java基础篇3

1 包装类型的缓存机制了解么?

Java 基本数据类型的包装类型的大部分都用到了缓存机制来提升性能。

Byte,Short,Integer,Long 这 4 种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据,Character 创建了数值在 [0,127] 范围的缓存数据,Boolean 直接返回 TRUE or FALSE。

对于 Integer,可以通过 JVM 参数 -XX:AutoBoxCacheMax=<size> 修改缓存上限,但不能修改下限 -128。实际使用时,并不建议设置过大的值,避免浪费内存,甚至是 OOM。

对于Byte,Short,Long ,Character 没有类似 -XX:AutoBoxCacheMax 参数可以修改,因此缓存范围是固定的,无法通过 JVM 参数调整。Boolean 则直接返回预定义的 TRUE 和 FALSE 实例,没有缓存范围的概念。

Integer 缓存源码:

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}
private static class IntegerCache {
    static final int low = -128;
    static final int high;
    static {
        // high value may be configured by property
        int h = 127;
    }
}

Character 缓存源码:

public static Character valueOf(char c) {
    if (c <= 127) { // must cache
      return CharacterCache.cache[(int)c];
    }
    return new Character(c);
}

private static class CharacterCache {
    private CharacterCache(){}
    static final Character cache[] = new Character[127 + 1];
    static {
        for (int i = 0; i < cache.length; i++)
            cache[i] = new Character((char)i);
    }

}

Boolean 缓存源码:

public static Boolean valueOf(boolean b) {
    return (b ? TRUE : FALSE);
}

如果超出对应范围仍然会去创建新的对象,缓存的范围区间的大小只是在性能和资源之间的权衡。

两种浮点数类型的包装类 Float,Double 并没有实现缓存机制。

Integer i1 = 33;
Integer i2 = 33;
System.out.println(i1 == i2);// 输出 true

Float i11 = 333f;
Float i22 = 333f;
System.out.println(i11 == i22);// 输出 false

Double i3 = 1.2;
Double i4 = 1.2;
System.out.println(i3 == i4);// 输出 false

2 说一下⾯向对象和⾯向过程的区别?

⾯向过程(POP):⾯向过程就是分析出解决问题所需要的步骤,然后⽤函数把这些步骤⼀步⼀步实现,
使⽤的时候再⼀个⼀个的⼀次调⽤就可以。
⾯向对象(OOP :⾯向对象,把构成问题的事务分解成各个对象,⽽建⽴对象的⽬的也不是为了完成⼀
个个步骤,⽽是为了描述某个事件在解决整个问题的过程所发⽣的⾏为。 ⽬的是为了写出通⽤的
代码,加强代码的重⽤,屏蔽差异性。

相比较于 POP,OOP 开发的程序一般具有下面这些优点:

  • 易维护:由于良好的结构和封装性,OOP 程序通常更容易维护。
  • 易复用:通过继承和多态,OOP 设计使得代码更具复用性,方便扩展功能。
  • 易扩展:模块化设计使得系统扩展变得更加容易和灵活。

POP 的编程方式通常更为简单和直接,适合处理一些较简单的任务。

⽤⼀个⽐喻:⾯向过程是编年体;⾯向对象是纪传体。

面向对象:

​
public class Circle {
    // 定义圆的半径
    private double radius;

    // 构造函数
    public Circle(double radius) {
        this.radius = radius;
    }

    // 计算圆的面积
    public double getArea() {
        return Math.PI * radius * radius;
    }

    // 计算圆的周长
    public double getPerimeter() {
        return 2 * Math.PI * radius;
    }

    public static void main(String[] args) {
        // 创建一个半径为3的圆
        Circle circle = new Circle(3.0);

        // 输出圆的面积和周长
        System.out.println("圆的面积为:" + circle.getArea());
        System.out.println("圆的周长为:" + circle.getPerimeter());
    }
}

​

定义了一个 Circle 类来表示圆,该类包含了圆的半径属性和计算面积、周长的方法。


面向过程:

public class Main {
    public static void main(String[] args) {
        // 定义圆的半径
        double radius = 3.0;

        // 计算圆的面积和周长
        double area = Math.PI * radius * radius;
        double perimeter = 2 * Math.PI * radius;

        // 输出圆的面积和周长
        System.out.println("圆的面积为:" + area);
        System.out.println("圆的周长为:" + perimeter);
    }
}

直接定义了圆的半径,并使用该半径直接计算出圆的面积和周长。

3 来说说面向对象有哪些特性?

①封装
封装把⼀个对象的属性私有化,同时提供⼀些可以被外界访问的属性的⽅法。
②继承
继承是使⽤已存在的类的定义作为基础创建新的类,新类的定义可以增加新的属性或新的⽅法,也
可以继承⽗类的属性和⽅法。通过继承可以很⽅便地进⾏代码复⽤。
关于继承有以下三个要点:
 1. ⼦类拥有⽗类对象所有的属性和⽅法(包括私有属性和私有⽅法),但是⽗类中的私有属性和⽅
法⼦类是⽆法访问,只是拥有。
 2. ⼦类可以拥有⾃⼰属性和⽅法,即⼦类可以对⽗类进⾏扩展。
 3. ⼦类可以⽤⾃⼰的⽅式实现⽗类的⽅法。
③多态
所谓多态就是指程序中定义的引⽤变量所指向的具体类型和通过该引⽤变量发出的⽅法调⽤在编程
时并不确定,⽽是在程序运⾏期间才确定,即⼀个引⽤变量到底会指向哪个类的实例对象,该引⽤
变量发出的⽅法调⽤到底是哪个类中实现的⽅法,必须在由程序运⾏期间才能决定。
Java 中有两种形式可以实现多态:继承(多个⼦类对同⼀⽅法的重写)和接⼜(实现接⼜并覆
盖接⼜中同⼀⽅法)。

创建一个对象用什么运算符?对象实体与对象引用有何不同?

new 运算符,new 创建对象实例(对象实例在堆内存中),对象引用指向对象实例(对象引用存放在栈内存中)。

  • 一个对象引用可以指向 0 个或 1 个对象(一根绳子可以不系气球,也可以系一个气球);
  • 一个对象可以有 n 个引用指向它(可以用 n 条绳子系住一个气球)。

5 对象的相等和引用相等的区别

  • 对象的相等一般比较的是内存中存放的内容是否相等。
  • 引用相等一般比较的是他们指向的内存地址是否相等。

举一个例子:

String str1 = "xiaoliang";
String str2 = new String("xiaoliang");
String str3 = "xiaoliang";
// 使用 == 比较字符串的引用相等
System.out.println(str1 == str2);
System.out.println(str1 == str3);
// 使用 equals 方法比较字符串的相等
System.out.println(str1.equals(str2));
System.out.println(str1.equals(str3));

大家可以来看看输出结果是什么?

输出:

false
true
true
true

从上面的代码输出结果可以看出:

  • str1 和 str2 不相等,而 str1 和 str3 相等。这是因为 == 运算符比较的是字符串的引用是否相等。
  • str1、 str2str3 三者的内容都相等。这是因为equals 方法比较的是字符串的内容,即使这些字符串的对象引用不同,只要它们的内容相等,就认为它们是相等的。

构造方法有哪些特点?是否可被 override?

构造方法具有以下特点:

  • 名称与类名相同:构造方法的名称必须与类名完全一致。
  • 没有返回值:构造方法没有返回类型,且不能使用 void 声明。
  • 自动执行:在生成类的对象时,构造方法会自动执行,无需显式调用。

构造方法不能被重写(override),但可以被重载(overload)。因此,一个类中可以有多个构造方法,这些构造方法可以具有不同的参数列表,以提供不同的对象初始化方式。

// 定义父类 Person
class Person {
    private String name;
    private int age;

    // 无参构造方法
    public Person() {
        this.name = "Unknown";
        this.age = 0;
        System.out.println("Person 类的无参构造方法被调用");
    }

    // 带参数的构造方法,构造方法重载
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("Person 类的带参数构造方法被调用");
    }

    public void introduce() {
        System.out.println("My name is " + name + ", and I'm " + age + " years old.");
    }
}

// 定义子类 Student 继承自 Person 类
class Student extends Person {
    private String studentId;

    // 无参构造方法
    public Student() {
        super(); // 调用父类的无参构造方法
        this.studentId = "Unknown";
        System.out.println("Student 类的无参构造方法被调用");
    }

    // 带参数的构造方法,构造方法重载
    public Student(String name, int age, String studentId) {
        super(name, age); // 调用父类的带参数构造方法
        this.studentId = studentId;
        System.out.println("Student 类的带参数构造方法被调用");
    }

    public void showStudentId() {
        System.out.println("My student ID is " + studentId);
    }
}

public class Main {
    public static void main(String[] args) {
        // 创建 Person 对象,调用无参构造方法
        Person person1 = new Person();
        person1.introduce();

        // 创建 Person 对象,调用带参数的构造方法
        Person person2 = new Person("Alice", 20);
        person2.introduce();

        // 创建 Student 对象,调用无参构造方法
        Student student1 = new Student();
        student1.introduce();
        student1.showStudentId();

        // 创建 Student 对象,调用带参数的构造方法
        Student student2 = new Student("Bob", 22, "123456");
        student2.introduce();
        student2.showStudentId();
    }
}

重载(overload)和重写(override)的区别?

⽅法的重载和重写都是实现多态的⽅式,区别在于前者实现的是编译时的多态性,⽽后者实现的是运⾏时的多态性。
重载 发⽣在⼀个类中,同名的⽅法如果有不同的参数列表(参数类型不同、参数个数不同或者⼆者
都不同)则视为重载;
重写 发⽣在⼦类与⽗类之间,重写要求⼦类被重写⽅法与⽗类被重写⽅法有相同的返回类型,⽐⽗
类被重写⽅法更好访问,不能⽐⽗类被重写⽅法声明更多的异常(⾥⽒代换原则)。
⽅法重载的规则:
1. ⽅法名⼀致,参数列表中参数的顺序,类型,个数不同。
2. 重载与⽅法的返回值⽆关,存在于⽗类和⼦类,同类中。
3. 可以抛出不同的异常,可以有不同修饰符。

访问修饰符 publicprivateprotected、以及不写(默认)时的区别?

Java 中,可以使⽤访问控制符来保护对类、变量、⽅法和构造⽅法的访问。 Java ⽀持 4 种不同的访问权限。
default ( 即默认,什么也不写) : 在同⼀包内可见,不使⽤任何修饰符。可以修饰在类、接⼜、 变量、⽅法。
private : 在同⼀类内可见。可以修饰变量、⽅法。 注意:不能修饰类(外部类)
public : 对所有类可见。可以修饰类、接⼜、变量、⽅法
protected : 对同⼀包内的类和所有⼦类可见。可以修饰变量、⽅法。 注意:不能修饰类(外部
类)
可见性privatedefaultprotectedpublic
同一个类中
同一个包中
子类中
全局范围

抽象类(abstract class)和接⼜(interface)有什么区别?

1. 接口的⽅法默认是 public ,所有⽅法在接⼜中不能有实现 (Java 8 开始接⼜⽅法可以有默认实
现),⽽抽象类可以有⾮抽象的⽅法。
2. 接⼜中除了 static final 变量,不能有其他变量,⽽抽象类中则不⼀定。
3. ⼀个类可以实现多个接⼜,但只能实现⼀个抽象类。接⼜⾃⼰本⾝可以通过 extends 关键字扩展 多个接口。
4. 接⼜⽅法默认修饰符是 public ,抽象⽅法可以有 public protected default 这些修饰符
(抽象⽅法就是为了被重写所以不能使⽤ private 关键字修饰!)。
5. 从设计层⾯来说,抽象是对类的抽象,是⼀种模板设计,⽽接⼜是对⾏为的抽象,是⼀种⾏为的
规范。
总结⼀下 jdk7~jdk9 Java 中接⼜的变化:
1. jdk 7 或更早版本中,接⼜⾥⾯只能有常量变量和抽象⽅法。这些接⼜⽅法必须由选择实现接
⼜的类实现。
2. jdk 8 的时候接⼜可以有默认⽅法和静态⽅法功能。
3. jdk 9 在接⼜中引⼊了私有⽅法和私有静态⽅法。

10 深拷贝和浅拷贝区别了解吗?什么是引用拷贝?

关于深拷贝和浅拷贝区别,这里先给结论:

  • 浅拷贝:浅拷贝会在堆上创建一个新的对象(区别于引用拷贝的一点),不过,如果原对象内部的属性是引用类型的话,浅拷贝会直接复制内部对象的引用地址,也就是说拷贝对象和原对象共用同一个内部对象。

  • 深拷贝:深拷贝会完全复制整个对象,包括这个对象所包含的内部对象。

上面的结论没有完全理解的话也没关系,我们来看一个具体的案例!

浅拷贝

浅拷贝的示例代码如下,我们这里实现了 Cloneable 接口,并重写了 clone() 方法。

clone() 方法的实现很简单,直接调用的是父类 Object 的 clone() 方法。

public class Address implements Cloneable{
    private String name;
    // 省略构造函数、Getter&Setter方法
    @Override
    public Address clone() {
        try {
            return (Address) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }
}

public class Person implements Cloneable {
    private Address address;
    // 省略构造函数、Getter&Setter方法
    @Override
    public Person clone() {
        try {
            Person person = (Person) super.clone();
            return person;
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }
}

测试一下:

Person person1 = new Person(new Address("武汉"));
Person person1Copy = person1.clone();
// true
System.out.println(person1.getAddress() == person1Copy.getAddress());

从输出结构可以看出, person1 的克隆对象和 person1 使用的仍然是同一个 Address 对象。


深拷贝

简单对 Person 类的 clone() 方法进行修改,连带着要把 Person 对象内部的 Address 对象一起复制。

@Override
public Person clone() {
    try {
        Person person = (Person) super.clone();
        person.setAddress(person.getAddress().clone());
        return person;
    } catch (CloneNotSupportedException e) {
        throw new AssertionError();
    }
}

测试一下:

Person person1 = new Person(new Address("武汉"));
Person person1Copy = person1.clone();
// false
System.out.println(person1.getAddress() == person1Copy.getAddress());

从输出结构就可以看出,显然 person1 的克隆对象和 person1 包含的 Address 对象已经是不同的了。


那什么是引用拷贝呢? 简单来说,引用拷贝就是两个不同的引用指向同一个对象。

综合来看,可以归纳为下面的图:


http://www.kler.cn/a/529653.html

相关文章:

  • TensorFlow 简单的二分类神经网络的训练和应用流程
  • Vue 3 30天精进之旅:Day 12 - 异步操作
  • MP4分析工具
  • 《深入分析 TNN、MNN 和 NCNN:为不同硬件平台挑选最佳深度学习框架》
  • Linux命令入门
  • 第一个Python程序
  • LLMs之DeepSeek:Math-To-Manim的简介(包括DeepSeek R1-Zero的详解)、安装和使用方法、案例应用之详细攻略
  • XML DOM 节点
  • 详解Kafka并行计算架构
  • 深入了解 ls 命令及其选项
  • 【AI】探索自然语言处理(NLP):从基础到前沿技术及代码实践
  • unity免费资源2025-2-2
  • 涡旋光特性及多种模型、涡旋光仿真
  • final-关键字
  • 穷举vs暴搜vs深搜vs回溯vs剪枝系列一>单词搜索
  • wax到底是什么意思
  • 【高级篇 / IPv6】(7.6) ❀ 03. 宽带IPv6 - ADSL拨号宽带上网配置 ❀ FortiGate 防火墙
  • 53. Uboot命令使用
  • 通过 Docker 部署 Mastodon 服务器 的教程
  • Vue.js 使用 `teleport` 实现全局挂载
  • 低成本、高附加值,具有较强的可扩展性和流通便利性的行业
  • 数据建模中的Chasm 陷阱
  • 更高效地使用Adobe软件,提升创作质量
  • Perl语言的函数实现
  • Echarts 封装通用组件
  • mysql大表的解决方案,及Hive分页查询