Java基础知识复习(一)

Java基础知识复习(一)

Java常见基础知识

1.Java基本功

1.1.1 Java语言有那些特点

  1. 简单易学(相比c++来说,Java容易上手);
  2. 面向对象(三大特性,封装,继承,多态);
  3. 跨平台(平台无关性,Java虚拟机实现与平台无关;
  4. 健壮性;
  5. 安全性;
  6. 体系结构中立(编译过的**.java**文件会变为字节码文件,这些字节码文件与特定的计算机结构体系无关);
  7. 多线程(Java是第一个支持并发程序设计的主流语言);
  8. 支持网络编程并且很方便( Java 语言诞生本身就是为简化网络编程设计的,因此 Java 语言不仅支持网络编程而且很方便);
  9. 高性能(编译与解释并存);

1.1.2 什么是JDK,JRE,JVM,他们又有什么区别

JVM(java virtual machine):Java虚拟机,Java程序都是运行再Java虚拟机上的

JRE(java runtime environment):Java的运行时环境,包括Java虚拟机,Java类库,Java命令以及其他的基础结构。但是它并不足以创建一个Java程序

JDK(java development kit):Java软件开发工具包(SDK),不仅包含了JRE和JVM,还具有编译器(javac)和工具(例如javadocjdb)。它能够创建和编译Java程序。

如果你只是为了运行一下 Java 程序的话,那么你只需要安装 JRE 就可以了。如果你需要进行一些 Java 编程方面的工作,那么你就需要安装 JDK 了。但是,这不是绝对的。有时,即使您不打算在计算机上进行任何 Java 开发,仍然需要安装 JDK。例如,如果要使用 JSP 部署 Web 应用程序,那么从技术上讲,您只是在应用程序服务器中运行 Java 程序。那你为什么需要 JDK 呢?因为应用程序服务器会将 JSP 转换为 Java servlet,并且需要使用 JDK 来编译 servlet。

1.1.3 Oracle JDK 和 Open JDK的对比

  1. Oracle JDK版本每三年发布一次,而OpenJDK版本每三个月发布一次。
  2. Oracle JDK将更加专注于稳定性,因为它更加重视其更多的企业用户,而Oracle JDK频繁发布以支持其他性能,这可能会导致不稳定。
  3. 与OpenJDK相比,Oracle JDK在响应能力和JVM性能方面提供了更好的性能。
  4. 从1.0开始,Oracle JDK的早期版本由Sun开发,后来由Oracle收购并为其他发行版维护,而OpenJDK最初仅基于Java SDK或JDK版本7。
  5. Open JDK 所有都是免费开源的,Oracle JDK 部分收费
  6. Oracle JDK 根据二进制代码许可协议获得许可,而 OpenJDK 根据 GPL v2 许可获得许可。

1.1.4 Java与C++的区别

  • 都是面向对象的语言,支持封装、继承、多态。
  • Java是C++的纯净版,没有头文件,没有复杂的指针,结构等,程序内存更加安全(不会出现野指针的情况)。
  • Java只支持单继承,C++支持多重继承。虽然Java的类不可以多重继承,但是接口可以多重继承。
  • Java有特有的垃圾回收机制,使得使用者无需手动释放内存,C++需要手动释放内存。

1.1.5 为什么说 Java 语言“编译与解释并存”?

高级程序语言按照程序的执行方式可以分为编译型和解释型两种。简单来说,编译型语言是指编译器针对特定的操作系统将源代码一次性翻译成可被该平台执行的机器码;解释型语言是指解释器对源程序逐行解释成特定平台的机器码并立即执行。

1.1.6面向对象的特征

三大特性:

  • **封装:**隐藏对象属性和实现细节,只对外提供访问方法或访问接口的技术。封装的目的是为了简化编程和增强程序的安全性,使得使用者只需要知道如何使用,无需了解到具体的实现细节。
  • 继承:继承是基于已存在的类构造一个新类的技术,继承已存在的类就是复用(继承)这些类的方法和域。在Java中,已存在的类称为超类(super class)基类(base class)父类(praent class),新类称为子类(sub class)派生类(derived class)。子类拥有父类的所有方法以及属性,但是,对于父类的私有属性和私有方法,子类只是拥有,并不能访问和使用继承的主要目的是为了代码复用,并且扩展更多的,更丰富的属性和功能。
  • **多态:**表示一个对象具有多种的状态。具体表现为父类的引用指向子类的实例。多态的目的是为了程序的可扩展性和维护性。 在Java中可以使用继承接口2大特性实现多态。

多态的特点:

  • 对象类型和引用类型之间具有继承(类)/实现(接口)的关系;
  • 对象类型不可变,引用类型可变;
  • 方法具有多态性,属性不具有多态性;
  • 引用类型变量发出的方法调用的到底是哪个类中的方法,必须在程序运行期间才能确定;
  • 多态不能调用“只在子类存在但在父类不存在”的方法;
  • 如果子类重写了父类的方法,真正执行的是子类覆盖的方法,如果子类没有覆盖父类的方法,执行的是父类的方法。

1.2 Java重要语法

这里只介绍一些重要的语法知识点,其他的基础语法我就不会说了。

1.2.1 重载和重写的区别

  • 重载: 重载是描述一个类中有多个方法名相同的方法,但是它们的参数,类型,返回值,参数的顺序可能不同,表现形式也就不同。
  • 重写: 重写是描述子类对父类的某个方法的逻辑进行了重新编写,但重写的只是方法的内容, 方法名,参数,类型,顺序,返回值都是不变的。

1.2.2 字符型常量和字符串常量的区别

  1. 形式上:字符常量是单引号引起的一个字符;字符串常量是双引号引起的若干字符。
  2. 含义上:字符常量相当于一个整型值(ASCII值),可以参加表达式运算;字符串常量代表一个地址值(该字符串在内存中存放位置)
  3. 占内存大小:字符常量只占2个自己;字符串常量占n个字节。

1.2.3 什么是泛型,什么是类型擦除

Java泛型(Generics) 是JDK5中引入的一个新特性,泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。

Java中的泛型伪泛型,在Java编译期间,所有的泛型信息都会被擦除,这就是通常所说的类型擦除。

List<Integer> list = new ArrayList<>();

list.add(12);
//这里直接添加会报错
list.add("a");
Class<? extends List> clazz = list.getClass();
Method add = clazz.getDeclaredMethod("add", Object.class);
//但是通过反射添加,是可以的
add.invoke(list, "kl");

System.out.println(list)

1.2.4 泛型通配符

常用的 T,E,K,V,?
  • ? 表示不确定的 java 类型
  • T (type) 表示具体的一个java类型
  • K V (key value) 分别代表java键值中的Key Value
  • E (element) 代表Element
? 无界通配符

一个抽象父类Animal和子类Dog,现在需要一个动物列表,我的第一反应是这样的:

List<Animal> listAnimals

但是老板的想法却是这样的:

List<? extends Animal> listAnimals

通配符在声明局部变量时是没有什么意义的,但是当你为一个方法设置声明一个参数时,它是非常重要的。

  public static void main(String[] args) {
        List<Dog> dogs = new ArrayList<>();

        countLegs(dogs);
        /*编译器会会报错,显示类型不匹配*/
        //countLegs1(dogs);
    }

    static int countLegs(List<? extends Animal> animals){
        int reVal = 0;
        for (Animal animal : animals) {
            reVal+= animal.getLegs();
        }
        return reVal;
    }

    static int countLegs1(List<Animal> animals){
        int reVal = 0;
        for (Animal animal : animals) {
            reVal+= animal.getLegs();
        }
        return reVal;
    }

所以,对于不确定或者不关心实际要操作的类型,可以使用无限制通配符(<?>),表示可以持有任意类型。像countLegs方法,规定了参数传入的上界,但是并不关心具体类型是什么,对于传入的参数,只要是Animal的子类,就都可以支持,而countLegs1方法不行。

为什么countLegs1方法就不行呢?Dog不是Animal的子类吗,根据多态的角度来讲,理论上应该是可以的,但是,在泛型的继承体系中,Dog并不算Animal的一个子类。

假设我们有以下代码:

animals的泛型是父类对象Animal,dogs的泛型是子类对象Dog,那么dogs转换animals能成功吗,我们知道子类对象转父类对象是可以的,但是子类泛型转父类泛型能成功吗,我们假设能成功。

 public static void main(String[] args) {
        List<Animal> animals = new ArrayList<Animal>();
        List<Dog> dogs = new ArrayList<Dog>();
        
        animals = dogs;
    }

那么animals就指向了一个泛型为Dog的集合容器,但是现在这个集合容器的泛型是Animal,看上去可以将另一个子类Cat加进容器中。

 public static void main(String[] args) {
        List<Animal> animals = new ArrayList<Animal>();
        List<Dog> dogs = new ArrayList<Dog>();

        animals = dogs;
        animals.add(new Cat(4));
    }

这段代码看上去好像没什么问题,但是仔细想想。现在animals指向的是一个泛型为Dog的容器,现在又将Cat放进容器中,就相当于在一堆狗里面放进一只猫,这就矛盾了。所以子类泛型不能转换为父类泛型,反过来也是一样。

上界通配符 <? extend E>

上界:用extend关键字指定,表示所指定的类型只能是某个类的子类或者这个类本身

 static <K extends Comparable,E extends Serializable> K test(K arg1,E arg2){
        K result = arg1;
        arg1.compareTo(arg2);

        return result;
    }
 // 表示既要实现Comparable接口,又要实现Serializable接口
static <K extends Comparable & Serializable> K test(K arg1) {
        K result = arg1;
        return result;
    }

有多个限定类型用"&“隔开,有多个类型变量用”,"逗号隔开

下界通配符 <? super E>

下界:用super关键字指定,表示所指定的类型只能是这个类本身或者某个类的父类,直至Object

static <T> void  test1(List<? super  T> dst ,List<T> list){
        for (T t : list) {
            dst.add(t);
        }
    }
?和 T的区别

?表示不确定的类型,常用于泛型方法的调用代码和形参,不能用于定义泛型类、泛型方法和泛型变量。

T 表示一个具体的类型,常用于泛型类和泛型方法的定义,可以定义泛型变量。

利用T来保证泛型的一致性
// 通过 T 来 确保 泛型参数的一致性,通常我们需要先声明
public <T extends Number> void
test(List<T> dest, List<T> src)

//通配符是 不确定的,所以这个方法不能保证两个 List 具有相同的元素类型,不需要提前声明
public void
test(List<? extends Number> dest, List<? extends Number> src)
类型参数可以多重限定而通配符不行
 static <T extends Comparable & Serializable> T test(T arg1) {
        T result = arg1;

        return result;
    }
?可以使用下界限定,但T不行

类型参数 T 只具有 一种 类型限定方式:

T extends A

但是通配符 ? 可以进行 两种限定:

? extends A
? super A

1.2.5 深拷贝vs浅拷贝

1、深拷贝:对基本数据类型进行值传递,对引用数据类型,拷贝所有内容,除了基本数据类型的变量复制一份,引用类型的变量也复制一份,并创建一个新的对象。

2、浅拷贝:对于基本数据类型进行值传递,对于引用数据类型直接返回这个引用本身。

2、Java面向对象

2.1 面向对象和面向过程的区别

  • 面向过程:面向过程性能比面向对象高。因为类的调用需要实例化,开销比较大,比较消耗资源,所以当性能是最重要的考量因素时,如单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发。
  • 面向对象:面向对象易维护、易复用、易扩展。所以面向对象比面向过程更加灵活或、更加容易维护。但是性能没有面向过程高。

2.2 构造器

2.1.1. 构造器Constructor是否可被override

Constructor不可以被override,但是可以被overload,所以可以在一个类中看到多个构造方法的情况。

2.1.2. 为什么要在一个类中定义一个不做事的无参构造方法

一个类在被初始化时,如果没有用super()来指定调用父类的某个构造方法(如果没有父类,默认是Object),则会调用父类中的无参构造方法(默认有一个隐式的super(),所谓隐式就是看不见)。所以,如果父类中只定义了有参构造方法,没有定义无参构造方法,此时就会发生编译错误。因为Java程序在父类中找不到可执行的无参构造方法。解决办法是在父类定义一个无参构造方法。

2.1.3. 一个类的构造方法的作用是什么。若没有在类中定义构造方法,对象是否能够被正确创建

作用是完成对类对象的初始化工作。若没有在类中定义构造方法,也会默认有一个隐式的无参构造方法。但如果我们自己写了构造方法(无论是否有参),Java就不会默认为我们添加无参的构造方法了。而且,如果我们只定义了有参构造方法,那么在创建对象的时候必须要传递参数了。所以我们创建一个类的时候,最好手动的吧无参构造方法写出来,不管它是否有用。

2.1.4. 在调用子类构造方法之前会先调用父类没有参数的构造方法,其目的是?

帮助子类做初始化工作。

2.2 面向对象的特性

这里这里之前写过了,就不再赘述。

2.3 抽象类和接口

2.3.1. 接口和抽象类的区别
  • 接口需要被实现,抽象类需要被继承
  • 一个类允许实现多个接口,但只允许继承一个抽象父类
  • 接口中的方法都是默认公共抽象的,抽象类中则既允许定义抽象方法也允许普通方法(jdk8中,接口被允许定义默认方法,jdk9中还允许定义私有方法)。
  • 接口是对类的规范,规范的是行为能力。抽象类是对类的抽象,抽象的是逻辑。

2.4 其他

2.4.1 String StringBuffer和StringBuilder的区别是什么?String为什么是不可变的?

String类中使用final关键字修饰字符数组来保存字符串(在Java9之后,使用byte数组存储字符串)

StringBuilderStringBuffer都继承自AbstractStringBuilder类,在 AbstractStringBuilder 中也是使用字符数组保存字符串char[]value 但是没有用 final 关键字修饰,所以这两种对象都是可变的(Java9后也是变成byte数组)。

StringBuilderStringBuffer中的所有方法实现基本上都是直接调用父类方法。

StringBuffer中的部分方法:

@Override
public synchronized char charAt(int index) {
    return super.charAt(index);
}

/**
 * @throws IndexOutOfBoundsException {@inheritDoc}
 * @since      1.5
 */
@Override
public synchronized int codePointAt(int index) {
    return super.codePointAt(index);
}

/**
 * @throws IndexOutOfBoundsException {@inheritDoc}
 * @since     1.5
 */
@Override
public synchronized int codePointBefore(int index) {
    return super.codePointBefore(index);
}

线程安全

String中的对象都是不可变的,可以理解为常量,线程安全。AbstractStringBuilderStringBuilderStringBuffer 的公共父类,定义了一些字符串的基本操作,如insert,append等,Stirngbuffer对重写的方法都加上了同步锁或对调用的方法加上了同步锁,所以是线程安全的。StringBuilder并没有对方法进行枷锁,所以它是非线程安全的。

性能

String由于是不可变的,每次操作都需要重新创建一个对象,所以效率比较低,StringBufferStringBuilder都是可变的字符串,但由于StringBuffer是线程安全的,所以在单线程环境下,效率会比StringBuilder稍微低点。

三者的使用:

操作少量数据,可以考虑用Stirng

在单线程环境下,使用StringBuilder

在多线程环境下,保证线程安全,使用StringBuffer

2.4.2 Object类的常见方法
 public final native Class<?> getClass();
 public native int hashCode();
 public boolean equals(Object obj)
 protected native Object clone() throws CloneNotSupportedException;
 public String toString()
 public final native void notify()
 public final native void notifyAll()
 public final void wait()
 public final native void wait(long timeoutMillis) throws InterruptedException; //让当前对象进入TIMED_WATING状态
 public final void wait(long timeoutMillis, int nanos) throws InterruptedException //让当前对象进入TIMED_WATING状态
 protected void finalize() throws Throwable

2.4.3 ==和equals的区别

==:它的作用是判断两个对象的地址是不是相等。即判断两个对象是不是属于同一个对象(引用同一块内存地址)。(基本数据类==比较的是值,引用数据类型比较的是内存地址)

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

equals():它的作用是判断两个对象是否相等,他不能用于比较基本数据类型。equals()方法存在于ObjectObject是所有类的直接或间接父类。

Objectequals()

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

从上面可以看出,如果需要使用equals()来比较两个对象是否相等,需要重写equals()

举个例子:

public class Test1 {
    public static void main(String[] args) {
        String a =new String("ab"); // 创建了两个对象,一个字符串"ab"对象,一个String对象
        String b = new String("ab");// 同理
        String aa = "ab"; // 创建了一个字符串"ab"放在常量池中
        String bb = "ab"; // 从常量池中查找,否则自己创建一个新的
        if (aa == bb) // true
            System.out.println("aa==bb");
        if (a == b) // false,非同一对象
            System.out.println("a==b");
        if (a.equals(b)) // true
            System.out.println("aEQb");
        if (42 == 42.0) { // true
            System.out.println("true");
        }
    }
}

说明:

  • String类已经重写了equals(),因为 Objectequals 方法是比较的对象的内存地址,而 Stringequals 方法比较的是对象的值。

String的equals()

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String aString = (String)anObject;
        if (coder() == aString.coder()) {
            return isLatin1() ? StringLatin1.equals(value, aString.value)
                              : StringUTF16.equals(value, aString.value);
        }
    }
    return false;
}
2.4.3 hashcode()与equals()

1. hashcode()

hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode() 定义在JDK的Object.java中,这就意味着Java中的任何类都包含有hashCode() 方法。hashcode方法一个Java本地方法,也就是说,这个方法可能是由c或c++语言实现的,用来将对象的地址计算后返回。

但是,只有当创建某个类的散列表时,hashcode()才有作用,其他情况下并没有作用。散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。(可以快速找到所需要的对象)

为什么重写 equals 时必须重写 hashCode 方法?

如果两个对象相等,则 hashcode 一定也是相同的。两个对象相等,对两个对象分别调用 equals 方法都返回 true。但是,两个对象有相同的 hashcode 值,它们也不一定是相等的 。因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖。

hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 class 的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)

为什么两个对象有一样的hashcode值,但两个对象不一定相等

因为hashcode()使用的算法可能会使不同的对象,恰巧计算出相等hashcode值,当两个对象hashcode值一样时,散列表会调用对象的equals()来比较两个对象的内容是否相等。也就是说hashcode只是用来缩小查找成本。

重写equals() 的规范

1)、自反性:对于任何非空引用x,x.equals(x)应该返回true。

2)、对称性:对于任何引用x和y。只有当y.equals(x)返回true,x.equals(y)也应该返回true。

3)、传递性:对于任何引用x、y和z,如果x.equals(y)返回true,y.equals(z)也应该返回true。

4)、一致性:如果x和y引用的对象没有发送变化,返回调用x.equals(y)应该返回同样的结果。

5)、对于任意非空引用x,x.equals(null)应该返回false。

2.4.4 Java序列化中如果有些字段不想进行序列化,怎么办?

对于不想进行序列化的变量,使用transient关键字修饰。

transient关键字的作用是:阻止实例中那些用此关键字修饰的变量序列化;当对象被反序列化时,被transient修饰的变量值不会被持久化和恢复。transient只能修饰变量,不能修饰类和方法。

2.4.5 自动拆箱和自动装箱

自动拆箱和自动装箱时Java编译器的一个语法糖。

自动装箱是指:将基本数据类型转为对应的包装类对象的过程。

自动拆箱是指:将包装类对象转为对应基本数据类型的过程。

自动装箱实际上调用了包装类对象的一个方法:valueof()

自动拆箱实际上调用了包装类对象的一个方法:intvalue()

在自动装箱时,处于节约内存的考虑,JVM会缓存处于缓存值范围内的对象。

Integer,Byte,Short,Long,Character包装类型具有缓存池, 而其他三种:Float,Double,Boolean不具有缓存池。

包装类的缓存池范围都在 -127~127之间,除了Character 在 0~127之间。

Reference

JavaGuide
Framework-learning
Java核心卷(一)

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值