Java基础(一)基础概念

1.Java中的几种基本数据类型是什么?对应的包装类型是什么?各自占用多少字节?

Java中有8种基本数据类型
byte      8位  1字节 包装类:Byte        默认值0        -128~127
short     16位 2字节 包装类:Short       默认值0        -32768(-2^15)~ 32767(2^15-1)
int       32位 4字节 包装类:Integer     默认值0        -2147483648 ~ 2147483647
long      64位 8字节 包装类:Long        默认值0L       -9223372036854775808(-2^63) ~ 9223372036854775807(2^63 -1)
char      16位 2字节 包装类:Character   默认值'u0000'  0 ~ 65535(2^16-1)
float     32位 4字节 包装类:Float       0f             1.4E-45 ~ 3.4928235E38
double    64位 8字节 包装类:Double      0d             4.9E-324 ~ 1.7976931348623157E308
boolean   1位        包装类:Boolean     false          true、false

2.String、StringBuffer和StringBuilder的区别是什么?String为什么是不可变的?

区别

可变性

  1. String:不可变。
  2. StringBuilder 和 StringBuffer 都继承自 AbstractStringBuilder类,类中用 char[] 字符数组来保存字符串,没有使用 final 和 private 关键字,所以这两个可变。

线程安全性

  1. String 中的对象不可变,可以看作是常量,线程安全。
  2. StringBuffer 线程不安全,没有对方法进行加同步锁。
  3. StringBuilder 线程安全,对方法加了同步锁或者对调用的方法加了同步锁,所以线程安全。

性能

  1. 每次操作 String 都会生成新 String 对象,并将指针指向新的 String对象
  2. StringBuffer 和 StringBuilder 每次都会对对象本身操作,StringBuilder 比 StringBuffer 性能提升 10% ~ 15% 左右。

使用场景

  1. 操作少量数据:String
  2. 单线程操作大量数据:StringBuilder
  3. 多线程操作大量数据:StringBuffer

3.String s1 = new String(“abc”); 这段代码创建了几个字符串对象?

  1. 两个;如果字符串常量池中不存在字符串对象“abc” 的引用,那么它会在堆上创建2个字符串对象,其中一个字符串对象的引用会背保存在字符串常量池中。
  2. 一个;如果字符串常量池中已存在字符串对象“abc” 的引用,则只会在堆中创建1个字符串对象“abc”。

4. == 与 equals ?hashCode 与 equals ?

== 对于基本类型和引用类型的作用效果不同。

  • 对于基本类型和引用类型,==比较的是值。
  • 对于引用数据类型来说,==比较的是对象的内存地址。

因为Java中只有值传递,所以,对于 == 来说,不管是比较基本数据类型,还是引用数据类型的变量,其本质比较的都是值,只是引用类型变量存的值是对象的地址。

equals() 不能用于判断基本数据类型的变量,只能用来判断两个对象是否相等。equals() 方法存在于 Object 类中,而 Object 类是所有类的直接与间接父类,因此所有的类都有 equals() 方法。 Object 类 equals() 方法

public boolean equals(Object obj) {
     return (this == obj);
}

equals() 方法存在两种使用情况

  • 类没有重写 equals() 方法: 通过 equals() 比较该类的两个对象时,等价于通过“==”比较这两个对象,使用的默认是 Object 类 equals() 方法。
  • 类重写了 equals() 方法: 一般我们都重写 equals() 方法来比较两个对象中的属性是否相等;若它们的属性相等,则返回true,认为这连个对象相等。

常见案例

String a = new String("ab"); // a 为一个引用
String b = new String("ab); // b 为另一个引用,对象的内容一样
String aa = "ab"; // 放在常量池中
String bb = "ab"; // 从常量池中查找
System.out.println(aa == bb); // true
System.out.println(a == b); // false
System.out.println(a.equals(b)); // true
System.out.println(42 == 42.0); // true

String 中 equals 方法是被重写过的,因为 Object 里比较的是内存地址,String 里比较的是值。以下是 String 类中的 equals() 方法源码。

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

hashCode() 有什么用?

hashCode() 的作用是获取海马(int 整数),也称为散列码。这个哈希码的作用是确定该对象在哈希表中的索引位置。 hashCode() 定义在JDK的 Object 类中,也就以为着 Java 中任何类都包含有 hashCode() 函数。 总结:

  • 如果两个对象的 hashCode 值相等,那这两个对象不一定相等(哈希碰撞)。
  • 如果两个对象的 hashCode 值相等并且 equals() 方法也返回 true ,我们才认为这两个对象相等。
  • 如果两个对象的 hashCode 值不相等,我们可以直接认为这两个对象不相等。

为什么重写 equals() 必须重写 hashCode?

  • equals 方法判断两个对象相等,那 hashCode 也要相等。
  • 两个对象有相同的 hashCode ,他们也不一定是相等的。

5.包装类型的缓存机制是什么?

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

6.自动装箱与拆箱是什么? 原理是什么?

  • 装箱:将基本类型用它们对应的引用类型包装起来。
  • 拆箱:将包装类型转换为基本数据类型。
Integer i = 10;  // 装箱
int n = i; // 拆箱
  • Integer i = 10 等价于 Integer i = Integer.valueOf(10);
  • int n = i 等价于 int n = i.initValue();
  • 在实际应用中,应避免频繁拆装箱操作。

7.深拷贝和浅拷贝的区别?什么是引用拷贝?

  • 浅拷贝:在堆上创建一个新的对象,如果原对象内部属性是引用类型的话,浅拷贝会直接复制内部对象的引用地址,也就是说浅拷贝对象和源对象公用一个内部对象。
  • 深拷贝:完全复制整个对象,包括这个对象所包含的内部对象。

浅拷贝实例代码,实现 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();
        }
    }
}

深拷贝示例代码

@Override
public Person clone() {
    try {
        Person person = (Person) super.clone();
        person.setAddress(person.getAddress().clone());
        return person;
    } catch (CloneNotSupportedException e) {
        throw new AssertionError();
    }
}
  • 什么是引用拷贝?:引用拷贝就是两个不同的引用指向同一个对象。
  • 总结:

image.png

8.Java注解的作用?

Annotataion(注解) 从Java5开始引入的新特性,可以看做是一种特殊的注释,用与修饰类、方法、变量等,提供某些信息供程序在编译或者运行时使用。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {

}

public interface Override extends Annotation{

}

9.Exception 和 Error 有什么区别?

  • Exception:程序本身可以处理的异常,可以通过catch进行捕捉。
  • Error:Error属于程序无法处理的错误,例如OOM,NCF等等。 Exception又分为 Checked Exception 和 Unchecked Exception,一个是编译期,如果受检查异常没有被catch或者throws,则没办法通过编译。另一种则是编译时不处理。

10.Java泛型是什么?什么是类型擦除?说一下常用的通配符?

  • Java泛型是 JDK5 引用的一个新特性,使用泛型可以增强代码的可读性及稳定性。编译器可以对泛型参数进行检测,并且通过泛型参数可以传入指定的对象类型。
  • 类型擦除:Java的泛型是伪泛型,因为在Java编译期间,所有的泛型信息都会被擦除掉
List<Integer> list = new ArrayList<>();
list.add(12);
// 编译期间报错
list.add("abc");
Class<? extends List> clazz = list.getClass();
Method add = clazz.getDeclaredMethod("add",Object.class);
// 运行期间通过
add.invoke(list,"abc");
System.out.println(list);
  • 通配符:常见的通配符有?和 T。
  • T 可以用于声明变量或常量,而 ? 不行。
  • T 一般用于声明泛型类或方法,通配符 ? 一般用于泛型方法的调用代码和形参。
  • T 在编译期会背擦除为限定类型或 Object,通配符用于捕获具体类型。

11.内部类了解吗?匿名内部类了解吗?

在Java中,内部类是指定义在另一个类内部的类。内部类可以访问其外部类的成员变量和方法,包括私有成员,因为它们在同一作用域内。内部类可以分为成员内部类、静态内部类、局部内部类和匿名内部类。

1.成员内部类:定义在外部类的内部,不使用static修饰。它可以访问外部类的所有成员,包括私有成员。

class OuterClass {
    private int x;

    class InnerClass {
        void display() {
            System.out.println("Value of x: " + x);
        }
    }
}

2.静态内部类:使用static修饰的内部类。它不持有对外部类对象的引用,因此无法访问外部类的非静态成员,但可以访问外部类的静态成员。

class OuterClass {
    static class StaticInnerClass {
        void display() {
            System.out.println("Inside static inner class");
        }
    }
}

3.局部内部类:定义在方法或作用域内部的类。局部内部类只在声明它的方法或作用域中可见

class OuterClass {
    void method() {
        class LocalInnerClass {
            void display() {
                System.out.println("Inside local inner class");
            }
        }
        LocalInnerClass inner = new LocalInnerClass();
        inner.display();
    }
}

4.匿名内部类:没有名字的内部类。通常用于创建实现某个接口或继承某个类的对象。

interface MyInterface {
    void display();
}

class OuterClass {
    void method() {
        MyInterface inner = new MyInterface() {
            @Override
            public void display() {
                System.out.println("Inside anonymous inner class");
            }
        };
        inner.display();
    }
}

匿名内部类的创建通常在需要实例化接口或抽象类的对象时使用,且仅使用一次,因此不需要命名。

12.Java反射是什么,有什么优缺点,怎么理解?

Java反射是指在运行时动态地获取类的信息(例如类的属性、方法、构造函数等),并且可以动态调用类的方法、创建对象,以及修改类的属性值。通过反射机制,可以在运行时获取程序中任意一个类的信息并操作它们,而无需在编译时确定这些信息。

优点:

  • 动态性:反射机制允许在运行时动态地获取和使用类的信息,使得程序具有更大的灵活性和可扩展性。
  • 泛化:通过反射可以编写通用的代码,对于不同的类都能适用,而不需要针对特定的类进行硬编码。
  • 解耦:反射使得类与类之间的依赖关系降低,提高了代码的灵活性和可维护性。

缺点:

  • 性能开销:由于反射涉及到动态的类加载、方法查找等操作,因此在性能上会有一定的开销,相比直接调用方法或创建对象,反射效率较低。
  • 类型安全性:由于反射允许绕过编译时的类型检查,因此可能导致类型安全性问题,如调用不存在的方法或访问私有属性等,这可能会导致程序运行时的异常。
  • 可读性和维护性:反射使得代码更加复杂和难以理解,因为它在编译时无法提供类型信息,导致代码可读性和维护性降低。

理解反射机制

反射机制在Java中可以通过java.lang.reflect包中的类来实现,例如Class、Method、Field等。通过这些类,可以获取类的信息,调用方法、设置属性等。理解反射机制的关键在于理解其灵活性和动态性,它使得在编译时无法确定类的情况下,依然能够进行类的操作,这为很多框架和工具的实现提供了可能。但同时,也要注意反射的性能开销和潜在的类型安全问题,合理使用反射才能发挥其优点并尽量避免其缺点。

13.BIO,NIO,AIO有什么区别?

BIO(Blocking I/O)、NIO(Non-blocking I/O)、AIO(Asynchronous I/O)是Java中用于处理I/O操作的三种不同的模型。

BIO(Blocking I/O):

  • 在BIO模型中,I/O操作是阻塞的。也就是说,当一个线程执行一个I/O操作时,它会被阻塞,直到操作完成或者出现错误。
  • 通常情况下,每个I/O操作都会对应一个线程,如果有大量的I/O操作需要处理,就需要创建大量的线程,而线程创建和销毁的开销很大,因此BIO模型在处理大量连接时效率较低。

NIO(Non-blocking I/O):

  • NIO模型是一种非阻塞的I/O模型。在NIO中,一个线程可以同时处理多个连接,不需要为每个连接创建一个线程。
  • NIO通过Selector(选择器)实现多路复用,Selector可以监听多个Channel,当某个Channel上的I/O事件发生时,就会通知对应的线程进行处理,从而实现了多个连接的复用。

AIO(Asynchronous I/O):

  • AIO模型是一种异步I/O模型。在AIO中,I/O操作不会阻塞线程,而是在操作完成后通过回调的方式通知应用程序。
  • AIO是在NIO的基础上进一步封装的,更加方便和高效地处理I/O操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值