目录

java基础知识整理

Java面向对象三大特性封装继承多态

封装

  • 封装把⼀个对象的属性私有化,同时提供⼀些可以被外界访问的属性的⽅法

继承

  • 继承是使⽤已存在的类的定义作为基础建⽴新类的技术,新类的定义可以增加新的数据或新的功能,
  • 也可以⽤⽗类的功能,但不能选择性地继承⽗类。通过使⽤继承我们能够⾮常⽅便地复⽤以前的代码。

多态

  • 指父类的某个方法被子类重写时,可以产生自己的功能行为,同一个操作作用于不同对象,可以有不同的解释,产生不同的执行结果。

  • 继承(多个⼦类对同⼀⽅法的重写)和接⼝(实现接⼝并覆盖接⼝中同⼀⽅法)。

Java的基本类型

基本数据类型是CPU可以直接进行运算的类型。Java定义了以下几种基本数据类型:

  • 整数类型:byte,short,int,long
  • 浮点数类型:float,double
  • 字符类型:char
  • 布尔类型:boolean

String是基本数据类型吗?答:不是

/img/Java的基本类型/1.png

String中有哪些方法?

常用方法:

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

Object有哪些方法?

  • Object类,属于java.lang包,位于类层次结构树的顶部。

  • 每个类都是Object类的直接或间接的后代。

  • 使用或编写的每个类都继承Object的实例方法。

常用方法:

  • getClass 方法 final 方法、获取对象的运行时 class 对象,class 对象就是描述对象所属类的对象。
  • hashCode 方法 该方法主要用于获取对象的散列值。Object 中该方法默认返回的是对象的堆内存地址。
  • equals 方法 该方法用于比较两个对象,如果这两个对象引用指向的是同一个对象,那么返回 true,否则返回 false。
  • clone 方法 该方法是保护方法,实现对象的浅复制,只有实现了 Cloneable 接口才可以调用该方法,否则抛出 CloneNotSupportedException 异常。
  • toString 方法 返回一个 String 对象,一般子类都有覆盖。默认返回格式如下:对象的 class 名称 + @ + hashCode 的十六进制字符串。
  • wait 方法 当timeout 为 0,即不等待。

==、Equals区别?

  1. 对象类型不同
  • equals():是超类Object中的方法。
  • ==:是操作符。
  1. 比较的对象不同
  • equals():用来检测两个对象是否相等,即两个对象的内容是否相等。
  • ==:用于比较引用和比较基本数据类型时具有不同的功能
  1. 运行速度不同
  • equals():没有==运行速度快。
  • ==:运行速度比equals()快,因为==只是比较引用。

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

  • 因为两个相等的对象的hashCode值必须是相等。
  • 也就是说如果equals方法判断两个对象是相等的,那这两个对象的hashCode值也要相等。
  • 如果重写equals()时没有重写hashCode()方法的话就可能会导致equals方法判断是相等的两个对象,hashCode值却不相等。

Final的作用?

在Java中,final关键字可以用来修饰类、方法和变量(包括成员变量和局部变量)。

  1. 修饰类

当用final修饰一个类时,表明这个类不能被继承。

  1. 修饰方法

如果只有在想明确禁止,该方法在子类中被覆盖的情况下才将方法设置为final的。

  • 即父类的final方法是不能被子类所覆盖的,也就是说子类是不能够存在和父类一模一样的方法的。
  1. 修饰变量
  • final成员变量表示常量,只能被赋值一次,赋值后值不再改变。
  • 当final修饰一个基本数据类型时,表示该基本数据类型的值一旦在初始化后便不能发生变化;
  • 如果final修饰一个引用类型时,则在对其初始化之后便不能再让其指向其他对象了,但该引用所指向的对象的内容是可以发生变化的。
  • final修饰一个成员变量(属性),必须要显示初始化。
  • 当函数的参数类型声明为final时,说明该参数是只读型的。即你可以读取使用该参数,但是无法改变该参数的值。

StringBuffer、StringBuilder区别?

  • StringBuffer稍慢,但线程安全
  • StringBuilder更快,但线程不安全,常用

抽象类和接口的区别

/img/java基础知识整理/img.png

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

首先我们面向过程的话,是我们去分析解决问题的一个步骤,然后用我们的函数,把这些步骤一步一步的实现, 然后我们在使用的时候,其实只要一一调用就可以,性能会比较高, 所以是咱们的单片机或者是嵌入式开发,一般都会用这个面向过程去开发。 而面向对象呢,这边的话是把我们构成问题的这些事物,分解成各个对象,就是其实也是万物皆对象, 那我们在建立这个对象的目的呢,其实也不是为了说完成一个个步骤,而是我们在描述某个事物, 然后再解决整个问题过程中发生的一些行为,所以我们面向对象,他们会有一些封装、继承、多态的一些特性, 所以他会比较容易维护,然后也会比较好复用比较好拓展,就是可以设计出一个我们的一个低耦合的一个系统, 但是性能上来说的话会比我们这个面向过程的话,会比要来的低,差不多是这样。

线程、程序、进程的基本概念,以及他们之间是一个怎么样的关系。

  • 线程其实跟我们的进程会有点相似,但是线程呢,他其实是一个比我们这个进程哦, 还要更小一点的一个执行单位,一个进程呢,在我们执行的时候的过程中,他会产生一个或多个的一个线程, 所以与我们这个进程不同的是,就是我们在同类的这个多线程,他共享同一块这个内存空间,和我们的一组这个系统资源, 所以这个系统呢,他也会产生一个线程或者说在各个线程之间呢,切换工作时,它的一个负担,会比我们这个进程要小的多, 也正是因为如此,所以线程他其实也会被称为一个轻量级的一个进程。那我们程序的话,其实就是指说,我们的含有指令的,和我们这个数据的这些文件, 那这些文件呢,是被存储在磁盘或者说其他数据呢,的一个存储设备中,也就是说我们这个,其实他更像一个静态的一个代码, 那我们进程就是我们程序,它的一次执行过程中呢,他是这个系统运行程序的一个最基本的一个单位,也正是因为如此, 所以这个进程其实他是一个动态的,就是我们系统如果运行一个程序的话,那就是一个进程,从我们的这个创建啊,然后到我们运行啊, 再到我们的消亡啊,这么一个过程,所以简单来说的话,这个一个进程其实就是一个执行中的一个程序, 他在计算机中的一个指令啊,就是一个指令接着一个指令去执行着,然后同时呢,每个进程呢,他还会占有这个某些系统资源的, 比如说我们的这个CPU的时间,内存空间啊,我们的文件啊,还有我们的输入输出设备的这些使用权啊,所以其实就是这个程序在执行的时候, 他都会被我们这个操作系统啊,载入到我们这个内存中,然后呢我们这个线程呢,他是进程里面划分的一个更小的一个运行单位, 所以线程和进程他有一个最大的不同就是在于,基本上这个每个进程都是一个独立的,但是每个线程就不一定了, 因为同一个进程中里面的这些线程,他们是非常有可能会影响到的,就是我们从另一个角度上来说的话,这个进程呢,他属于操纵系统的范畴, 然后如果主要是同一时间段内啊,然后我们可以同时执行一个或以上的一个程序, 然后这个线程呢,就是在我们同一程序内,几乎是同时执行一个以上的一个程序段,差不多是这样。

深拷贝和浅拷贝的区别?

  • 如果是浅拷贝的话,就是我们被复制的对象,这里面所有的变量,大多含与我们这个原来的对象,会有相同的值, 然后所有的,对其他的一个引用呢,他热然是指向我们这个原来的这些对象, 其实简单说的话就是浅拷贝,他仅仅复制了我们所考虑的对象,而不是复制了他所引用的对象。 那如果是我们的深拷贝的话,就是他被复制对象的这些所有变量啊,都是含有跟原来的这个对象相同的值, 那些被引用其他对象的变量呢,就会将指向我们这个被复制的这个新对象,而不是原有的那些被引用的这么一个对象, 其实简单说的话就是我们的一个,深拷贝就是把我们这个复制的对象,所引用的对象呢,都复制了一遍,差不多就是这样。

Cookie和Session的区别?

  • 那我们首先就先说一下这个Cookie嘛,Cookie这边,是以这个客户端浏览器用来保存我们数据的一种机制, 好那就是当我们通过这个浏览器去进行我们这个网页访问的时候呢,服务器这边可以把我们某些数据,以这个key-value啊,就是我们那个键值对嘛, 以这种方式写入到我们的Cookie里面,然后存储到我们这个客服端的浏览器, 然后我们客服端下一次再去访问这个服务器的时候,就可以携带这些状态数据,发送到我们的服务器端, 然后我们服务器端里面,这边就可以根据我们的Cookie,Cookie里面携带的内容,去识别我们这个,具体是哪位客户的一个使用者。 那如果是我们的Session的话,就是我们的一个会话,他是属于我们服务端的一个容器对象, 那么在默认情况下啊,就是默认情况下我们这个Session,他会针对我们每个浏览器的一个请求,就是我们的这个Servlet的这个容器, 他都会分配一个Session,我们Session其实他本质上是一个我们的一个ConcurrentHashMap,然后可以存储到我们当前会话,产生的一些状态数据, 嗯,因为我们的这个HTTP协议啊,他本省其实是一个无状态的一个协议,那这种无状态协议也就是说我们服务器他,并不会去知道我们的客户端, 发送过来的一个多次请求,是不是属于同一个用户的,所以我们Session就是来弥补这个HTTP,这个无状态的一个不足嘛,就是我们这个服务器端, 他可以用我们的Session去存储我们的客户端在同一个会话里面的一个多次的一个请求记录,然后,基于我们这个服务端的Session的这些储存机制的话, 在结合我们客服端的这个Cookie机制,然后就可以实现我们这个有状态的一个HTTP协议了,总的来看的话,其实Cookie就是我们这个客服端的一个存储机制, 然后Session是我们服务端的一个存储机制,这两者去结合使用,来去实现我们一个规划的存储。以上是我这个问题的理解。

token

  • token类似一个令牌,无状态,用户信息都被加密到token中,服务器收到token后解密就可知道是哪个用户。需要开发者手动添加。

重载和重写有什么区别?

  • 重载的话都是在一个类里面的,就是方法名相同的,然后这里面呢,是他们都可能说是参数不同,返回类型也是可以不同的, 那每个重载的方法,或者说这个构造函数,构造函数也是有这个的,都会有一个独一无二的一个参数, 类型的一个列表,那这个最常用的就是我们这个构造器的重载, 比如说我们的有参构造啊,我们的无参构造啊,这些都是最常用的,或者说不同参数的这些构造方法。 重写的话呢,是发生在这个面向对象里面的那个继承,就是发生在我们的父类和子类之间,方法名相同,然后它的一个参数列表也是相同的, 返回值的这个范围就是小于等于我们的父类,然后抛出异常的这个范围,也是小于等于我们的父类,然后他这边的话,就是访问我们的修饰符的范围, 是大于等于我们的父类的,如果我们父类这个方法啊,如果我们父类方法修饰符为private,就是私有化,那么子类就不能重新我们这个方法, 因为他都不能继承到这个方法。然后这边的话,我们的重载和重写都是多态的一种体现,的一种方式嘛,就是我们重载是编译期间的一个活动, 然后重写是我们这个运行期间的一个活动,然后就是我们这个重载是在一个类中定义的这个相同名字和方法,然后就是这个方法的参数列表和类型都要不同, 然后返回型也可以不同,是我们一个重写的标准,然后也可以修改为可见性,然后重写是不一样的,他要求我们这个子类去重写这个基类的方法时候, 要跟父类方法具有相样的,就是基本上都要一样,那他的一个可见性是需要大于等于我们这个基类的一个方法,差不多就是这样。

/img/重写和重载的作用?/1.png

IO流一般分为几种?

  • 如果是按照功能来分的话,一个就是我们的输入流(input),还有就是我们的输出流(output)嘛, 然后如果是按类型来分的话,就是也有分我们的子节流啊,和我们的字符流,那子节流和我们的字符流他们也有一些区别嘛, 子节流他是按照我们的一个8位传输,是以字节为单位的,这么一个输入输出的我们的数据,然后我们的字符流,是以16位的, 然后是以字符为单位的,去输入输出我们的这些数据。

静态变量和普通变量这一块的区别?

  • 静态变量的话就是我们static的变量嘛,他们的区别就是说静态变量他这是我们所有的一个对象是共享的, 因为我们在项目运行的时候,他在内存中就只有一个副本,想这个副本的话, 就是说我们在程序初次加载的时候,会被初始化嘛,然后普通变量是在我们对象里面的,他是对象里面所拥有的,就是我们在创建对象的时候,会被初始化, 然后呢他就会有很多的副本,因为我们可能这边new一个对象那边new一个对象,他就存在很多个这个普通的一个变量, 但是这个各个对象之间,他们存在多个副本的话,就各个对象里面这个副本,他们也是互相不会影响的, 然后还有一点就是说我们的static我们的这个静态变量,它的一个初始化顺序,是按照我们定义的这个顺序去进行初始化的,就这些,这就是他们的区别。

自增运算符++和自减运算符

  • ++ 和 – 运算符可以放在变量之前,也可以放在变量之后
  • 当运算符放在变量之前时(前缀),先自增/减,再赋值 ++a
  • 当运算符放在变量之后时(后缀),先赋值,再自增/减 a--

例如

  • 当 b = ++a 时,先自增(自己增加 1),再赋值(赋值给 b)

  • 当 b = a++ 时,先赋值(赋值给 b),再自增(自己增加 1)

  • ++a 输出的是 a+1 的值,a++输出的是 a 值

  • “符号在前就先加/减,符号在后就后加/减”

Java的参数传递是传值还是传引用?

  • Java世界中的一切对象都是指针(地址)
  • 函数调用永远是传值
  1. 基本类型(包括String类)作为参数传递时,是传递值的拷贝,无论你怎么改变这个拷贝,原值是不会改变的
  2. 引用类型(包括数组,对象以及接口)作为参数传递时,是把对象在内存中的地址拷贝了一份传给了参数。
  • 注意:基本数据类型的封装类Integer、Short、Float、Double、Long、Boolean、Byte、Character虽然是引用类型, 但它们在作为参数传递时,也和基本数据类型一样,是值传递。

Continue、break和return的区别是什么?

在循环结构中,当循环条件不满足或者循环次数达到要求时,循环会正常结束。 但是,有时候可能需要在循环的过程中,当发生了某种条件之后 ,提前终止循环, 这就需要用到下面几个关键词:

  • continue:指跳出当前的这一次循环,继续下一次循环。
  • break:指跳出整个循环体,继续执行循环下面的语句。

return:用于跳出所在方法,结束该方法的运行。return 一般有两种用法:

  • return; :直接使用 return 结束方法执行,用于没有返回值函数的方法
  • return value; :return 一个特定值,用于有返回值函数的方法

下列语句的运行结果是什么?

 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
 public static void main(String[] args) {
        boolean flag = false;
        for (int i = 0; i <= 3; i++) {
            if (i == 0) {
                System.out.println("0");
            } else if (i == 1) {
                System.out.println("1");
                continue;
            } else if (i == 2) {
                System.out.println("2");
                flag = true;
            } else if (i == 3) {
                System.out.println("3");
                break;
            } else if (i == 4) {
                System.out.println("4");
            }
            System.out.println("xixi");
        }
        if (flag) {
            System.out.println("haha");
            return;
        }
        System.out.println("heihei");
    }

运行结果:

1
2
3
4
5
6
7
0
xixi
1
2
xixi
3
haha

Java异常

异常基本类型

  • 异常类的基本类型是Throwable
  • 两大子类分别是ErrorException

Error

  • 系统错误由Java虚拟机抛出,用Error类表示。Error类描述的是内部系统错误
  • 例如:Java虚拟机崩溃。在程序中不会对Error异常进行捕捉和抛出。

Exception

异常Exception又分为RuntimeException(运行时异常)和CheckedException(检查时异常)

RuntimeException(运行时异常)
  • 程序运行过程中才可能发生的异常,一般为代码的逻辑错误
  • 例如:类型错误转换,空指针异常、找不到指定类等
CheckedException(检查时异常)
  • 编译期间可以检查到的异常,必须显式的进行处理(捕获或者抛出到上一层)
  • 例如:IOException, FileNotFoundException,SQLException

异常处理

throws(声明异常)

  • 在方法头中显式声明该异常,以便于告知方法调用者此方法有异常
  • 若父类的方法没有声明异常,则子类继承方法后,也不能声明异常

try-catch(捕获异常)

  • 若执行try块的过程中没有发生异常,则跳过catch子句
  • 若是出现异常,try块中剩余语句不再执行。
  • 再判断catch块的异常类是否是捕获的异常类型,匹配后执行相应的catch块中的代码。
  • 如果有finally的话进入到finally里面继续执行。
  • try ctach fianally中有return时,会先执行return,但是不会返回。在执行完finally后进行返回
  • catch语句可以有一个或多个,finally语句最多一个

throw,throws关键字区别

  • throw关键字是用于方法体内部,用来抛出一个Throwable类型的异常
  • throws关键字用于方法体外部的方法声明部分,用来声明方法可能会抛出某些异常
1
2
3
public static void test()throws Exception{
    throw new Exception("方法test中的Exception");
}  

常见的几种异常

RuntimeExceptionjava.lang.ArithmeticException(算术异常)

  • java.lang.NullPointerException(空指针异常)
  • java.lang.ClassCastException(类型转换异常)
  • java.lang.IllegalArgumentException(不合法的参数异常)
  • java.lang.IndexOutOfBoundsException(数组下标越界异常)

java.io.IOException(IO流异常)

  • java.lang.ClassNotFoundException(没找到指定类异常)
  • java.lang.NoSuchFieldException(没找到指定字段异常)
  • java.lang.NoSuchMetodException(没找到指定方法异常)
  • java.lang.IllegalAccessException(非法访问异常)
  • java.lang.InterruptedException(中断异常)

静态方法为什么不能调用非静态方法?

  1. 静态方法是属于类的,在类加载的时候就会分配内存,可以通过类名直接访问。而非静态成员属于实例对象,只有在对象实例化之后才存在,需要通过类的实例对象去访问。
  2. 在类的非静态成员不存在的时候静态成员就已经存在了,此时调用在内存中还不存在的非静态成员,属于非法操作。

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

调用方式

  • 在外部调用静态方法时,可以使用类名.方法名的方式,也可以使用对象.方法名的方式,而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象
  • 不过,需要注意的是一般不建议使用对象.方法名的方式来调用静态方法。这种方式非常容易造成混淆,静态方法不属于类的某个对象而是属于这个类。

因此,一般建议使用类名.方法名的方式来调用静态方法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public class Person {
    public void method() {
      //......
    }

    public static void staicMethod(){
      //......
    }
    
    public static void main(String[] args) {
        Person person = new Person();
        // 调用实例方法
        person.method();
        // 调用静态方法
        Person.staicMethod()
    }
}

访问类成员是否存在限制

  • 静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),不允许访问实例成员(即实例成员变量和实例方法),而实例方法不存在这个限制。