• java八股文复习-----2024/03/04----基础


    相关资源
    大彬八股文


    2024八股文


    2024秋招八股文


    1.了解Java的包装类型吗?为什么需要包装类?

    Java 是一种面向对象语言,很多地方都需要使用对象而不是基本数据类型。比如,在集合类中,我们是无法将 int 、double 等类型放进去的。因为集合的容器要求元素是 Object 类型。

    为了让基本类型也具有对象的特征,就出现了包装类型。相当于将基本类型包装起来,使得它具有了对象的性质,并且为其添加了属性和方法,丰富了基本类型的操作。

    List list=new ArrayList();
    
    list集合如果要放整数的话,只能放对象,不能放基本类型,因此需要将整数自动装箱成对象。
    
    • 1
    • 2
    • 3

    2.自动装箱和拆箱

    Java中基础数据类型与它们对应的包装类见下表:

    原始类型包装类型
    booleanBoolean
    byteByte
    charCharacter
    floatFloat
    intInteger
    longLong
    shortShort
    doubleDouble

    装箱:将基础类型转化为包装类型。

    拆箱:将包装类型转化为基础类型。

    当基础类型与它们的包装类有如下几种情况时,编译器会自动帮我们进行装箱或拆箱:

    赋值操作(装箱或拆箱)
    进行加减乘除混合运算 (拆箱)
    进行>,<,==比较运算(拆箱)
    调用equals进行比较(装箱)
    ArrayList、HashMap等集合类添加基础类型数据时(装箱)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    示例代码:

    Integer x = 1; // 装箱 调⽤ Integer.valueOf(1)
    int y = x; // 拆箱 调⽤了 X.intValue()
    
    • 1
    • 2

    下面看一道常见的面试题:

    Integer a = 100;
    Integer b = 100;
    System.out.println(a == b);
    
    Integer c = 200;
    Integer d = 200;
    System.out.println(c == d);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    输出:

    true
    false
    
    • 1
    • 2

    为什么第三个输出是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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    Integer c = 200; 会调用 调⽤Integer.valueOf(200)。而从Integer的valueOf()源码可以看到,这里的实现并不是简单的new Integer,而是用IntegerCache做一个cache。

    private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];
    
        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;
        }
     
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    这是IntegerCache静态代码块中的一段,默认Integer cache 的下限是-128,上限默认127。当赋值100给Integer时,刚好在这个范围内,所以从cache中取对应的Integer并返回,所以a和b返回的是同一个对象,所以比较是相等的,当赋值200给Integer时,不在cache 的范围内,所以会new Integer并返回,当然比较的结果是不相等的。

    3.int和Integer有什么区别

    实现原理:javac编译器的语法糖,底层是通过Integer.valueOf()和Integer.intValue()方法实现。

    区别:

    Integer是int的包装类,int则是java的一种基本数据类型
    Integer变量必须实例化后才能使用,而int变量不需要
    Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值
    Integer的默认值是null,int的默认值是0

    4.String能被继承吗? 为什么用final修饰 ?

    不能被继承,因为String类有final修饰符,而final修饰的类是不能被继承的。

    String 类是最常用的类之一,为了效率,禁止被继承和重写。
    为了安全。String 类中有native关键字修饰的调用系统级别的本地方法,调用了操作系统的 API,如果方法可以重写,可能被植入恶意代码,破坏程序。Java 的安全性也体现在这里。


    5.String 为什么不可变?

    先看看什么是不可变的对象。

    如果一个对象,在它创建完成之后,不能再改变它的状态,那么这个对象就是不可变的。不能改变状态的意思是,不能改变对象内的成员变量,包括基本数据类型的值不能改变,引用类型的变量不能指向其他的对象,引用类型指向的对象的状态也不能改变。

    接着来看Java8 String类的源码:

    public final class String
        implements java.io.Serializable, Comparable<String>, CharSequence {
        /** The value is used for character storage. */
        private final char value[];
    
        /** Cache the hash code for the string */
        private int hash; // Default to 0
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    从源码可以看出,String对象其实在内部就是一个个字符,存储在这个value数组里面的。

    value数组用final修饰,final 修饰的变量,值不能被修改。因此value不可以指向其他对象。

    String类内部所有的字段都是私有的,也就是被private修饰。而且String没有对外提供修改内部状态的方法,因此value数组不能改变。

    所以,String是不可变的。

    那为什么String要设计成不可变的?

    主要有以下几点原因:

    1.线程安全。同一个字符串实例可以被多个线程共享,因为字符串不可变,本身就是线程安全的。
    2.支持hash映射和缓存。因为String的hash值经常会使用到,比如作为 Map 的键,不可变的特性使得 hash 值也不会变,不需要重新计算。
    3.出于安全考虑。网络地址URL、文件路径path、密码通常情况下都是以String类型保存,假若String不是固定不变的,将会引起各种安全隐患。比如将密码用String的类型保存,那么它将一直留在内存中,直到垃圾收集器把它清除。假如String类不是固定不变的,那么这个密码可能会被改变,导致出现安全隐患。
    4.字符串常量池优化。String对象创建之后,会缓存到字符串常量池中,下次需要创建同样的对象时,可以直接返回缓存的引用。

    既然我们的String是不可变的,
    它内部还有很多substring, replace, replaceAll这些操作的方法。
    这些方法好像会改变String对象?怎么解释呢?
    
    • 1
    • 2
    • 3

    其实不是的,我们每次调用replace等方法,其实会在堆内存中创建了一个新的对象。然后其value数组引用指向不同的对象

    6.String, StringBuffer 和 StringBuilder区别

    1. 可变性

    String 不可变
    StringBuffer 和 StringBuilder 可变
    2. 线程安全

    String 不可变,因此是线程安全的
    StringBuilder 不是线程安全的
    StringBuffer 是线程安全的,内部使用 synchronized 进行同步


    StringBuffer 与 StringBuilder 中的方法和功能完全是等价的,

    只是StringBuffer 中的方法大都采用了 synchronized 关键字进行修饰,因此是线程安全的,而 StringBuilder 没有这个修饰,可以被认为是线程不安全的。

    在单线程程序下,StringBuilder效率更快,因为它不需要加锁,不具备多线程安全而StringBuffer则每次都需要判断锁,效率相对更低

    7.String 类的常用方法有哪些?

    indexOf():返回指定字符的索引。
    charAt():返回指定索引处的字符。
    replace():字符串替换。
    trim():去除字符串两端空白。
    split():分割字符串,返回一个分割后的字符串数组。
    getBytes():返回字符串的 byte 类型数组。
    length():返回字符串长度。
    toLowerCase():将字符串转成小写字母。
    toUpperCase():将字符串转成大写字符。
    substring():截取字符串。
    equals():字符串比较。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    8.String s = “xyz” 和 String s = new String(“xyz”) 区别?

    两个语句都会先去字符串常量池中检查是否已经存在 “xyz”,如果有则直接使用,如果没有则会在常量池中创建 “xyz” 对象

    另外,String s = new String(“xyz”) 还会通过 new String() 在堆里创建一个内容与 “xyz” 相同的对象实例。

    所以前者其实理解为被后者的所包含。

    9.基本类型有哪些转换方式?

    分为自动类型转换和强制类型转换两种。

    1.自动类型转换:是一种小类型到大类型的转换
    2.强制类型转换:需要在强制类型转换的变量前面加上括号,然后在括号里面标注要转换的类型。

    在这里插入图片描述

    10.&和&&的区别?

    & 是按位与运算符,
    && 是短路与运算符,它只在第一个操作数为true时才计算第二个操作数。

    & 可以用于任何类型的操作数:
    && 只能用于布尔类型的操作数。

    11.java数据类型

    在这里插入图片描述

    12.JVM执行过程

    在这里插入图片描述

    13.那String一般都存储在JVM的哪块区域呢?

    字符串在JVM中的存储分两种情况,

    一种是String对象,存储在JVM的堆栈中。

    一种是字符串常量,存储在常量池里面。

    14.什么情况下字符串会存储在常量池呢?

    当通过字面量进行字符串声明时,

    比如String s = “程序新大彬”;,这个字符串在编译之后会以常量的形式进入到常量池。

    15.Object常用方法有哪些?

    Java面试经常会出现的一道题目,Object的常用方法。下面给大家整理一下。

    Object常用方法有:toString()、equals()、hashCode()、clone()等。

    toString

    默认输出对象地址。

    public class Person {
        private int age;
        private String name;
    
        public Person(int age, String name) {
            this.age = age;
            this.name = name;
        }
    
        public static void main(String[] args) {
            System.out.println(new Person(18, "程序员大彬").toString());
        }
        //output
        //me.tyson.java.core.Person@4554617c
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    可以重写toString方法,按照重写逻辑输出对象值。

    public class Person {
        private int age;
        private String name;
    
        public Person(int age, String name) {
            this.age = age;
            this.name = name;
        }
    
        @Override
        public String toString() {
            return name + ":" + age;
        }
    
        public static void main(String[] args) {
            System.out.println(new Person(18, "程序员大彬").toString());
        }
        //output
        //程序员大彬:18
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    equals
    默认比较两个引用变量是否指向同一个对象(内存地址)。

    public class Person {
        private int age;
        private String name;
    
        public Person(int age, String name) {
           this.age = age;
           this.name = name;
        }
    
        public static void main(String[] args) {
            String name = "程序员大彬";
            Person p1 = new Person(18, name);
            Person p2 = new Person(18, name);
    
            System.out.println(p1.equals(p2));
        }
        //output
        //false
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    可以重写equals方法,按照age和name是否相等来判断:

    public class Person {
        private int age;
        private String name;
    
        public Person(int age, String name) {
            this.age = age;
            this.name = name;
        }
    
        @Override
        public boolean equals(Object o) {
            if (o instanceof Person) {
                Person p = (Person) o;
                return age == p.age && name.equals(p.name);
            }
            return false;
        }
    
        public static void main(String[] args) {
            String name = "程序员大彬";
            Person p1 = new Person(18, name);
            Person p2 = new Person(18, name);
    
            System.out.println(p1.equals(p2));
        }
        //output
        //true
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    hashCode
    将与对象相关的信息映射成一个哈希值,默认的实现hashCode值是根据内存地址换算出来。

    public class Cat {
        public static void main(String[] args) {
            System.out.println(new Cat().hashCode());
        }
        //out
        //1349277854
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    clone
    Java赋值是复制对象引用,如果我们想要得到一个对象的副本,使用赋值操作是无法达到目的的。Object对象有个clone()方法,实现了对象中各个属性的复制,但它的可见范围是protected的。

    protected native Object clone() throws CloneNotSupportedException;

    所以实体类使用克隆的前提是:

    实现Cloneable接口,这是一个标记接口,自身没有方法,这应该是一种约定。调用clone方法时,会判断有没有实现Cloneable接口,没有实现Cloneable的话会抛异常CloneNotSupportedException。
    覆盖clone()方法,可见性提升为public。

    public class Cat implements Cloneable {
        private String name;
    
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    
        public static void main(String[] args) throws CloneNotSupportedException {
            Cat c = new Cat();
            c.name = "程序员大彬";
            Cat cloneCat = (Cat) c.clone();
            c.name = "大彬";
            System.out.println(cloneCat.name);
        }
        //output
        //程序员大彬
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    getClass
    返回此 Object 的运行时类,常用于java反射机制。

    public class Person {
        private String name;
    
        public Person(String name) {
            this.name = name;
        }
    
        public static void main(String[] args) {
            Person p = new Person("程序员大彬");
            Class clz = p.getClass();
            System.out.println(clz);
            //获取类名
            System.out.println(clz.getName());
        }
        /**
         * class com.tyson.basic.Person
         * com.tyson.basic.Person
         */
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    wait
    **当前线程调用对象的wait()方法之后,当前线程会释放对象锁,进入等待状态。**等待其他线程调用此对象的notify()/notifyAll()唤醒或者等待超时时间wait(long timeout)自动唤醒。线程需要获取obj对象锁之后才能调用 obj.wait()。

    notify
    obj.notify()唤醒在此对象上等待的单个线程,选择是任意性的
    notifyAll()唤醒在此对象上等待的所有线程。

    16.讲讲深拷贝和浅拷贝?

    浅拷贝:拷⻉对象和原始对象的引⽤类型引用同⼀个对象。

    以下例子,Cat对象里面有个Person对象,调用clone之后,克隆对象和原对象的Person引用的是同一个对象,这就是浅拷贝。

    public class Cat implements Cloneable {
        private String name;
        private Person owner;
    
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    
        public static void main(String[] args) throws CloneNotSupportedException {
            Cat c = new Cat();
            Person p = new Person(18, "程序员大彬");
            c.owner = p;
    
            Cat cloneCat = (Cat) c.clone();
            p.setName("大彬");
            System.out.println(cloneCat.owner.getName());
        }
        //output
        //大彬
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    深拷贝:拷贝对象和原始对象的引用类型引用不同的对象。

    以下例子,在clone函数中不仅调用了super.clone,而且调用Person对象的clone方法(Person也要实现Cloneable接口并重写clone方法),从而实现了深拷贝。可以看到,拷贝对象的值不会受到原对象的影响。

    public class Cat implements Cloneable {
        private String name;
        private Person owner;
    
        @Override
        protected Object clone() throws CloneNotSupportedException {
            Cat c = null;
            c = (Cat) super.clone();
            c.owner = (Person) owner.clone();//拷贝Person对象
            return c;
        }
    
        public static void main(String[] args) throws CloneNotSupportedException {
            Cat c = new Cat();
            Person p = new Person(18, "程序员大彬");
            c.owner = p;
    
            Cat cloneCat = (Cat) c.clone();
            p.setName("大彬");
            System.out.println(cloneCat.owner.getName());
        }
        //output
        //程序员大彬
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    浅拷贝:只复制某个对象的引用,而不复制对象本身,新旧对象还是共享同一块内存

    深拷贝:深拷贝会创造一个一摸一样的对象,新对象和原对象不共享内存,修改新对象不会改变原对对象。

  • 相关阅读:
    Python实现WOA智能鲸鱼优化算法优化BP神经网络回归模型(BP神经网络回归算法)项目实战
    创建自己的函数库
    『力扣每日一题13』:给定一个整数sum,从有N个有序元素的数组中寻找元素a,b,使得a+b的结果最接近sum,最接近sum,最快的平均时间复杂度是?
    hadoop使用笔记
    LeetCode 209. 长度最小的子数组
    Spring MVC如何进行重定向呢?
    Power BI 和 Tableau的区别
    企业数据管理中,细节至关重要
    From Sparse to Dense: GPT-4 Summarization with Chain of Density Prompting
    常识 | dds通信
  • 原文地址:https://blog.csdn.net/m0_68185369/article/details/136447005