java学习日记 2020.2.14

193 阅读15分钟

java基础

java面向对象编程三大特性:封装、继承、多态

封装

封装就是把属性私有化,用方法去访问这些属性。不过不想让属性被访问,就可以不提供对应的方法。当然,一个访问方法都没有的类并没有什么意义。

继承

继承是使用已存在的类的定义建立新的类。新的类可以定义新的属性或方法。值得注意的是,子类不能选择性的继承父类。
1、子类拥有父类对象所有的属性和方法(包括私有属性和方法),但父类中的私有属性和方法子类是无法访问的,只能拥有
2、子类可拥有自己属性和方法
3、子类可以用自己的方式实现父类的方法(重写,不过如果父类的方法是final类型的就不能重写了)。

多态

多态指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。 java有两种形式可以实现多态:继承(多个子类对同一方法的重写)和接口(实现接口并覆盖接口中同一方法)

为什么在静态方法内调用一个非静态成员是非法的?

如下代码:

public class StaticMethod {
    static int s1=1;
    int s2=1;
    static void StaticCall(){
        System.out.println(s1);
    }
    static void NStaticCall(){
        System.out.println(s2);
    }
    static void main(String a[]){
        StaticCall();
        NStaticCall();
    }

}

会出现如上图错误。 由于静态方法可以不通过对象进行调用,因此静态方法里,不能调用其他非静态变量,也不可以访问非静态变量成员。静态方法和静态成员是属于类的,而非静态方法和成员是属于实例化的对象的。静态方法和成员在类加载时而被分配内存,是这个类创建的所有对象共享的资源,可以直接用类名调用。而非静态方法和成员是在类实例化具体的对象后被分配内存的,不同的实例他的非静态方法和成员是不共享的,对象私有,需要有具体的实例对象名才能调用具体的成员变量和方法。

总结原因:
1、静态方法和成员先于非静态方法和成员创建,拿先创建的东西去调用还没创建成员自然无法。
2、静态方法无法通过非静态的成员变量名去判断是哪一个对象的成员变量被调用。这就好比每个班级都有座位号,你无法在不告知班级的情况下说出20号座位是哪个学生。

在java中定义一个不做事且没有参数的构造方法的作用

java程序在执行子类的构造方法之前,如果没有用super()来调用父类特定的构造方法,则会调用父类中“没有参数的构造方法”。因此,如果父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用super()来调用父类中特定的构造方法,则编译时发生错误,因为父类中找不到没有参数的构造方法可供执行。解决办法是在父类里加上一个不做事且没有参数的构造方法。

import java和javax有什么区别?

实际上java和javax没区别。都是一个名字。刚开始javaAPI所需的包是java开头的包,javax是作为扩展API使用的。随着时间的推移,javax逐渐成为javaAPI的组成部分。由于使两者合并太麻烦,所以决定将javax包作为标准API的一部分。javax里的类比较新。

接口和抽象类的区别是什么?

1、接口的方法默认是public,所有方法在接口中不能有实现(java8开始接口方法可以有默认实现),而抽象类可以有非抽象的方法。

2、接口中除了static、final变量,不能有其他变量,而抽象类不一定。

3、一个类可以实现多个接口,但只能实现一个抽象类。接口自己本身可以通过extends关键字扩展出多个接口

4、接口方法默认修饰符是public,抽象方法可以有public、protected和default这些修饰符(抽象方法本身就是为了重写,所以不会有private关键字)

5、从设计层面来说,抽象是对类的抽象,是一种模板的设计,而接口是对行为的抽象,是一种行为规范。

ps:这里接口作为行为规范的作用就好比我有一个路由器的接口,里面定义了交换、路由、广播等这些路由器需要去实现的功能。所有实现这种路由器接口的类,都可以知道它们有路由器的功能,且调用的方法名就是接口里的方法名。

成员变量和局部变量的区别有哪些?

成员变量在类被创建的时候就被创建,并且可以在该类的范围内使用。局部变量属于方法内的变量,随方法的创建而创建,随方法的消失而消失。局部变量只在方法内有效。

被final修饰的方法可否被继承?

可以被继承,不能被覆盖。也就是说无法重写。
但如果final修饰的是变量,则表明该变量是一个常数,只读。而且可以通过子类将父类final变量覆盖。

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

用new运算符,new创建对象实例(对象实例在堆内存中),对象引用指向对象实例(对象引用存放在栈内存中)。一个对象引用可以指向一个对象实体,也可以不指向。一个对象实体可以有多个对象引用指向它。

什么是方法的返回值?返回值在类的方法里的作用是什么?

一个类的构造方法的作用是什么?若一个类没有声明构造方法,该程序能正确执行吗?为什么?

构造方法有哪些特性?

静态方法和实例方法有何不同?

对象的相等与指向他们的引用相等,两者有什么不同?

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

==与equals

hashCode与equals

什么是hashCode()

hashCode的作用就是获取哈希码,也叫散列码。它实际上是一个int类型的整数。这个哈希码的作用是确定该对象在哈希表中的位置。

哈希表是什么?
哈希表是一种数据结构,通过相应的哈希码确定对象在表中的位置,存储的是键值对。键就是哈希码,值就是数据对象的值。

为什么要有hashCode

确定对象在哈希表中的位置。hashcode在散列表中才有用(索引位置),其他情况下只是个整数。
举个例子,在把对象加入HashSet时,HashSet会先计算该对象的哈希值来判断加入的位置,同时比较其他对象的hashcode值是否和加入对象的哈希值相等。如果相等,再判断俩对象是否相同(equals方法),相同则不加入新对象,不同则将新对象重新散列到其他位置(这么做的原因是减少equals次数,提高执行速度)。如果表中对象的哈希值和加入的对象哈希值不同,则说明要加入的位置可以加入。

hashCode()与equals()相关规定

两个对象相等时,hashcode一定相等
两个对象不等时,hashcode不一定相等
hashcode相等时,两个对象不一定相等
hashcode不等时,两个对象一定不等
equals方法被覆盖时,hashCode方法也必须被覆盖。

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

equals和hashcode都是java.lang.Object类的两个重要方法,一下是Object类里这两个方法的源码。

public native int hashCode()public boolean equals(Object paramObject){
    return (thi
![](https://user-gold-cdn.xitu.io/2020/2/23/17070e3beea09d8e?w=600&h=377&f=jpeg&s=52244)s == paramObject);
}

可以看到,在没有重写equals方法时,equals实际上和==是等价的,都是比较地址值,通过地址值的相等来判断对象相等。但很多时候我们需要判断对象是否相等,若地址值不一样而对象一样,我们也认为是相等。这种情况下就需要重写equals方法。 如果只重写equals方法而不重写hashCode方法,则调用的hashCode方法是Object的方法,Object里的hashCode方法是通过地址值算的哈希值,故会得到一个矛盾:两个地址值不同的相同对象s1和s2,在重写的equals方法里s1和s2是相等的,而两者算出的hash值却是不一样的(因为地址值不同)。也就是说相同对象的hashCode值不一样了。

关于hashcode的一些规定:

两个对象相等时,hashcode一定相等
两个对象不等时,hashcode不一定相等
hashcode相等时,两个对象不一定相等
hashcode不等时,两个对象一定不等

为什么说java中只有值传递?

在java中,基本数据类型作为参数传入函数中时,实际上是将参数拷贝了一份传入函数,函数内对参数的所有操作都是对复制品的操作,并不会影响到实参。这种传递方式被成为值传递。而当参数是引用数据类型时,传入的是一个对象的地址值的拷贝,通过这个拷贝,我们能影响到实际参数(对象),看着好像是引用传递,但引用传递是直接将地址值传入,而不是先拷贝一个再将复制的地址值传入。先拷贝再传入复制的值属于值传递,只不过对于基本数据类型,复制传递的是对象本身的值,而对于引用数据类型,复制传递的是地址值(引用)。

用电脑的快捷方式比较好理解。
值传递:
对于基本数据类型:将一个文件复制一份,然后函数修改复制的文件。(对实参无影响)
对于引用数据类型:将一个文件的快捷方式复制一份,函数可以通过复制的快捷方式修改原文件,也可以修改复制的快捷方式,但不会影响到原先的快捷方式。(对实参有影响)

引用传递:
将一个文件的快捷方式传入,函数通过快捷方式可以影响到原文件,也可以修改快捷方式。(对实参有影响)

简述线程、程序、进程的基本概念。以及他们之间关系是什么?

程序: 是含有指令和数据的文件,被存储在磁盘或其他数据存储设备中,也就是说程序是静态的代码。
进程: 是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。进程是系统运行一个程序从创建到消亡的过程。一个进程就是一个执行中的程序。
线程: 是比进程更小的执行单位,是代码段的执行过程。一个进程中可以产生多个线程。和进程是相对独立的不同,不同的线程极有可能相互影响。由于在执行代码中,不同的代码段共享同一块内存或一组资源。为了防止冲突,必须将这些代码段作为一个小部分来考虑,也就是作为线程来考虑资源竞争的问题。线程也叫轻量级进程。

线程有哪些基本状态?

NEW: 初始状态,线程被构建但还没开始运行。
RUNNABLE: 运行状态,java线程将操作系统中的就绪和运行都称作“运行中”
BLOCKED: 阻塞状态,表示线程阻塞于锁。
WAITING: 等待状态,需要等待其他线程做出一些特定动作(通知或中断)才能恢复。
TIME_WAITING: 超时等待状态,等待一段时间后自行恢复。
TERMINATED: 终止状态,表示当前线程已经执行完毕。

关于final关键字的一些总结

final用来修饰类的时候,表示该类不能被继承。俗称断子绝孙类。final类里的所有成员方法都会隐式地指定为final方法。

final用来修饰方法时。表示该方法不能被重写,方法的内容不允许被子类修改。

final用来修饰变量时,若变量为基本数据类型时,必须初始化,其数值一旦初始化后便不再更改。若变量为引用数据类型时,表示该引用不能再指向其他对象。

ps: 若父类和子类出现同名变量,子类的变量会覆盖父类的变量吗?不会,父类的变量依旧存在。只不过需要用到父类中的访问方法,若子类重写了父类的访问方法,那么访问的变量就是子类的变量。(这里也体现出了final的用途,用于访问父类中与子类同名变量)

java中的异常处理

java中所有的异常都在java.lang包中的Throwable类。这个类有两个子类ErrorException。两者又包含了大量的子类。

Error: 程序无法处理的错误

Exception: 程序可以处理的异常
RuntimeException(jvm抛出异常),NullPointerException(要访问的变量没有引用任何对象),ArithmeticException(算术运算异常),ArrayIndexOutOfBoundsException(下标越界异常)

Error和Exception的区别: 一个可以被程序处理(异常),一个不能被程序处理(错误)。异常如除0异常(算术运算异常ArithmeticException)。错误如java虚拟机运行错误中的内存溢出错误(OutOfMemoryError)jvm不再拥有继续执行操作的内存。

java异常类层次结构图

throwable类常用方法

异常处理总结

try块 用于捕获异常。其后可接0个或多个catch块,如果没有catch块,则必须跟一个finally块。

catch块 用于处理try捕获到的异常。

finally块 无论是否捕获或处理异常,finally块里的语句都会被执行。当try或catch块中遇到return语句时,finally语句会在方法返回之前被执行。

注意: 当try语句和finally语句中都有return语句时,在方法返回之前,finally语句的内容将被执行,并且finally语句的返回值会覆盖原始的返回值。如下:

public static int f(int value){
    try{
        return value*value;
    }finally{
        if(value == 2){
            return 0;
        }
    }
}

如果调用f(2),返回值会是0,因为finally语句的返回值覆盖了try语句块的返回值。如果finally不用if的话,那么一直是finally的返回值。

在以下四种情况下,finally块不会执行

1、finally块第一句就出现异常。

2、前面的代码中用了System.exit(int)已退出程序。若该语句是在异常语句之后的,finally还是会执行。

3、程序所有的线程死亡。

什么情况下线程会全部死亡?

4、关闭cpu

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

用transient关键字进行修饰。表示该变量不作为字节序列而存储在磁盘里或传输。transient表示暂时的意思。

获取用键盘输入常用的两种方法

1、用Scanner类

Scanner input = new Scanner(System.in);
String s = input.nextLine();
input.close;

这里Scanner需要实例化对象。

2、通过BufferedReader

BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
String s = input.readLine();

这两种方法有什么区别?

1、结束标志不一样: Scanner对象在输入回车、空格、Tab键时会结束输入;而BufferedReader对象只会在按下回车时结束输入。也就是说,如果要输入的字符串里包含了空格或tab,则Scanner不合适。

2、包含他们的包不一样:
Scanner类包含在java.util包里。BufferedReader类包含在java.io包里。使用BufferedReader对象的readLine()方法就需要处理IO异常(IOException)。

3、效率: BufferedReader的效率比Scanner的高。BufferedReader是字符输入流中读取文本,故支持字符、数组、行的高效读取。

Scanner类中提供了很多方法:
next():取得一个字符串
nextInt():将取得的字符串转换成int类型的整数。 nextFloat():将字符串转换成float类型。

java中IO流

java中IO流分几种

既然有了字节流,为什么还要有字符流

BIO,NIO,AIO有什么区别

外部类和内部类

常见关键字总结:static,final,this,super

static修饰的变量是可以用static方法修改值的。static修饰的变量属于类,而不属于某个实例。所以用起来是Calss.method();

属于实例的方法用起来是Class instance = new Class(); instance.method();(instance是实例的意思)

Collections工具类和Arrays工具类常见方法总结

深拷贝和浅拷贝