java基础

目录

一、java基础

1.语言特点

2.JDK

3.字节码(java编译成.class,实现跨平台)

3.1跨平台

4. Java 语言“编译与解释并存”

5.基础语法

5.1数据类型

5.1.1基础

5.1.2引用类型:

6.自动类型转换、强制类型转换?

7.自动拆箱/封箱?

8.&(逻辑与)和&&(短路与)有什么区别?

9.switch case 是否能作用在 byte/long/String 上?

10.break ,continue ,return 的区别及作用?

11.用最有效率的方法计算 2 乘以 8?

12.自增自减运算?

二、面向对象

1.三大特性

2.重载(overload)和重写(override)的区别?

3.访问修饰符 public、private、protected、以及不写(默认)

4.this 关键字有什么作用?

5.抽象类(abstract class)和接口(interface)有什么区别?

6.成员变量与局部变量的区别''

7.静态变量和实例变量的区别?静态方法、实例方法呢?

8.final 关键字有什么作用?

9.final、finally、finalize 的区别?

10.==和 equals 的区别?

11.hashCode 与 equals?

12.是值传递,还是引用传递?

13.深拷贝和浅拷贝?(怎么实现)

14.创建对象4方法

三,String

0.String 引用类型,不可继承

1.String 和 StringBuilder、StringBuffer 的区别?

3.String str1 = new String("abc")和 String str2 = "abc" 和 区别?

4.字符串拼接是如何实现的?

5.intern 方法

四,Integer?

1.Integer a= 127,Integer b = 127;Integer c= 128,Integer d = 128;,相等吗?

2.String 怎么转成 Integer 的?原理?

五,Object

常见方法

六,异常

处理方法

#41.三道经典异常处理代码题

七,IO

分类

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

BIO、NIO、AIO?

八, 序列化

什么是序列化?什么是反序列化?

三种序列化

九,泛型?

十, 注解

十一,反射

十二,API

Math

String?

构造方法

判断功能

获取功能

替换功能

System

Object

toString()

equals() ?

hashcode()

日期

包装类


一、java基础

面向对象的编程语言,不仅吸收了 C++语言的各种优点,还摒弃了 C++里难以理解的多继承、指针等概念

1.语言特点

面向对象(封装,继承,多态),跨平台(虚拟机),多线程,安全,简单,分布式,高性能,编译与解释并存

封装,继承,多态,抽象

2.JDK

JDK、JRE、JVM关系

JDK 包含 JRE,JRE 包含 JVM

3.字节码(java编译成.class,实现跨平台)

3.1跨平台

Java 语言编写的程序,一次编译后,可以在多个系统平台上运行。

实现原理:Java 程序是通过 Java 虚拟机在系统平台上运行的

java 程序经过编译之类产生的.class 文件,字节码能够被虚拟机识别,从而实现 Java 程序的跨平台性

  • 编译:将我们的代码(.java)编译成虚拟机可以识别理解的字节码(.class)

  • 解释:虚拟机执行 Java 字节码,将字节码翻译成机器能识别的机器码

  • 执行:对应的机器执行二进制机器码

    Java程序执行过程

编译与解释

4. Java 语言“编译与解释并存”

编译:一次性翻译。编译器针对特定的操作系统将源代码一次性翻译成可被该平台执行的机器码

解释:逐行解释,立即执行。解释器对源程序逐行解释成特定平台的机器码并立即执行。

Java 程序要经过先编译((javac)Java 编译器产生.class),后解释(JVM解释成对应操作系统机器码)两个步骤。

5.基础语法

5.1数据类型

5.1.1基础
  • 数值型

    • 整数类型(byte、short、int、long)

      字节:1 2 4 8

      位数:8 16 32 64

    • 浮点类型(float、double)

      字节: 4 8

      位数:32 64

    • 字符型(char)

      字节:2

      位数:16

    • 布尔型(boolean)

      位数:1

5.1.2引用类型:
  • 类(class)

  • 接口(interface)

  • 数组([])

6.自动类型转换、强制类型转换?

Java自动类型转换方向

在Java中,浮点数默认为双精度类型,也就是说,如果直接使用3. 4这样的数字来声明一个浮点型变量,Java会将其默认为双精度类型。

float f=3.4,对吗?

不正确。3.4 是双精度数,将双精度型(double)赋值给浮点型(float)属于下转型(down-casting,也称为窄化)会造成精度损失,因此需要强制类型转换float f =(float)3.4;或者写成float f =3.4F

short s1 = 1; s1 = s1 + 1;有错吗?

short s1 = 1; s1 += 1;有错吗?

对于short s1 = 1; s1 = s1 + 1;由于1是int类型,因此s1+1运算结果也是int 型,需要强制转换类型才能赋值给short型。

而short s1 = 1; s1 += 1;可以正确编译,因为s1+= 1;相当于s1 = (short)(s1 + 1);其中有隐含的强制类型转换。最后结果还是short

(x += i)只是表达式(x = x + i)的简写方式,这并不准确。这两个表达式都被称为赋值表达式。第二个表达式使用的是简单赋值操作符(=),而第一个表达式使用的是复合赋值操作符+=。

Java语言规范中讲到,复合赋值(E1 op=E2)等价于简单赋值(E1=(T)((E1) op (E2))),其中T是E1的类型,除非E1只被计算一次。复合赋值表达式自动地将所执行计算的结果转型为其左侧变量的类型。如果结果的类型与该变量的类型相同,那么这个转型不会造成任何影响。然而,如果结果的类型比该变量的类型要宽,那么复合赋值操作符将悄悄地执行一个窄化原生类型转换,存在危险

7.自动拆箱/封箱?

  • 装箱:将基本类型用它们对应的引用类型包装起来;

  • 拆箱:将包装类型转换为基本数据类型;

    装箱和拆箱

    Integer i = 10;  //装箱
    int n = i;   //拆箱
    ​

    装箱和拆箱。 作用:为了保证通用性和提高系统性能 一种最普通的场景是调用一个包含类型为Object的参数的函数(方法),该Object可支持任意 类型,以便通用。当你需要将一个值类型传入容器时,就需要装箱了。 另一种的用法,就是一个泛型 的容器,同样是为了保证通用,而将元素定义为Object类型的,将值类型的值加入该容器时,需要装箱。

8.&(逻辑与)和&&(短路与)有什么区别?

&和 | 既是逻辑运算符也是位运算符,而&&和||只是逻辑运算符

当使用&运算符: 计算机在判断表达式的值的时候,先判断3>5 的值为假,然后再判断3>2的结果为真,于是最后的结果是 假&真 为假;

但是当我们使用&&运算符的时候:计算机先判断3>5 的值为假,此时表达式的结果一定为假,所以计算机就不再往下判断了,判定表达式结果为假。

9.switch case 是否能作用在 byte/long/String 上?

switch(expr)中,expr 能是 byte、short、char、int,也可以是枚举 enum 类型。 还可以是字符串(String),但是长整型(long)在目前所有的版本中都是不可以的。

10.break ,continue ,return 的区别及作用?

  • break 跳出整个循环,不再执行循环(结束当前的循环体)

  • continue 跳出本次循环,继续执行下次循环(结束正在执行的循环 进入下一个循环条件)

  • return 程序返回,不再执行下面的代码(结束当前的方法 直接返回)

11.用最有效率的方法计算 2 乘以 8?

2 << 3。位运算,数字的二进制位左移三位相当于乘以 2 的三次方

12.自增自减运算?

b = ++a 时,先自增(自己增加 1),再赋值(赋值给 b);当 b = a++ 时,先赋值(赋值给 b),再自增(自己增加 1)。也就是,++a 输出的是 a+1 的值,a++输出的是 a 值。

int i  = 1;
i = i++;
System.out.println(i);
结果为1
​

JVM 而言,它对自增运算的处理,是会先定义一个临时变量来接收 i 的值,然后进行自增运算,最后又将临时变量赋给了值为 2 的 i,相对于下面代码:

int i = 1;
int temp = i;
i++;
i = temp;
System.out.println(i);
​

二、面向对象

1.三大特性

  • 封装

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

  • 继承 子类 extends 父类

    继承是使⽤已存在的类的定义作为基础创建新的类,新类的定义可以增加新的属性或新的方法,也可以继承父类的属性和方法。通过继承可以很方便地进行代码复用。

    只有单继承,没有多继承

    super this

    super 关键字的用法和 this 关键字的用法相似

    this:代表本类对象的引用(this关键字指向调用该方法的对象一般我们是在当前类中使用this关键字所以我们常说this代表本类对象的引用) super:代表父类存储空间的标识(可以理解为父类对象引用)

关于继承有以下三个要点:

  1. ⼦类拥有⽗类对象所有的属性和⽅法(包括私有属性和私有⽅法),但是⽗类中的私有属性和⽅法⼦类是⽆法访问,只是拥有。

  2. ⼦类可以拥有⾃⼰属性和⽅法,即⼦类可以对⽗类进⾏扩展。

  3. ⼦类可以⽤⾃⼰的⽅式实现⽗类的⽅法。

  • 多态

    ⼀个引⽤变量到底会指向哪个类的实例对象,该引⽤变发出的⽅法调⽤到底是哪个类中实现的⽅法,必须在由程序运⾏期间才能决定。

三大条件:

  1. 继承:必须存在继承

  2. 重写:子类必须对父类中的方法进行重写;

  3. 向上转型:通过父类的引用调用子类的重写的方法;父类的引用指向子类的对象,实际上就是创建一个子类对象,将其当作父类对象来使用;

    编译类型看定义对象时 = 号的左边,运行类型看 = 号的右边:父类类型 引用名 = new 子类类型();

    Animal cat = new Cat("多多",1);
    //向上转型,将Cat类型转换为Animal类型
    //直接赋值实现向上转型
    @Override   //重写               
        public void eat() { //向上转型
            System.out.println(this.name + "吃狗粮");
        }
     //通过方法传参来实现向上转型,也可以通过方法返回来实现向上转型。

    在 Java 中有两种形式可以实现多态:继承(多个⼦类对同⼀⽅法的重写)和接⼝(实现接⼝并覆盖接⼝中同⼀⽅法)

2.重载(overload)和重写(override)的区别?

都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。

  • 重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;

  • 重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的返回类型

3.访问修饰符 public、private、protected、以及不写(默认)

访问修饰符和可见性

4.this 关键字有什么作用?

this 是自身的一个对象,代表对象本身,可以理解为:指向对象本身的一个指针

this 的用法在 Java 中大体可以分为 3 种:

  1. 普通的直接引用,this 相当于是指向当前对象本身

  2. 形参与成员变量名字重名,用 this 来区分:

public Person(String name,int age){
    this.name=name;
    this.age=age;
}

3.引用本类的构造函数

5.抽象类(abstract class)和接口(interface)有什么区别?

  1. 接⼝的⽅法默认是 public ,所有⽅法在接⼝中不能有实现(Java 8 开始接⼝⽅法可以有默认实现),⽽抽象类可以有⾮抽象的⽅法。

  2. 接⼝中除了 static 、 final 变量,不能有其他变量,⽽抽象类中则不⼀定。

  3. ⼀个类可以实现多个接⼝,但只能实现⼀个抽象类。接⼝⾃⼰本身可以通过 extends 关键字扩展多个接⼝。

  4. 接⼝⽅法默认修饰符是 public ,抽象⽅法可以有 public 、 protected 和 default 这些修饰符(抽象⽅法就是为了被重写所以不能使⽤ private 关键字修饰!)。

  5. 从设计层⾯来说,抽象是对类的抽象,是⼀种模板设计,⽽接⼝是对⾏为的抽象,是⼀种⾏为的规范。

6.成员变量与局部变量的区别''

变量在内存中的⽣存时间上看:成员变量是对象的⼀部分,它随着对象的创建⽽存在,⽽局部变量随着⽅法的调⽤⽽⾃动消失。

在内存中的存储⽅式来看:如果成员变量是使⽤ static 修饰的,那么这个成员变量是属于类的,如果没有使⽤ static 修饰,这个成员变量是属于实例的。对象存于堆内存,如果局部变量类型为基本数据类型,那么存储在栈内存,如果为引⽤数据类型,那存放的是指向堆内存

成员变量如果没有被赋初值会⾃动以类型的默认值⽽赋值

7.静态变量和实例变量的区别?静态方法、实例方法呢?

静态变量和实例变量的区别?

静态变量: 是被 static 修饰符修饰的变量,也称为类变量,它属于类,不属于类的任何一个对象,静态变量在内存中有且仅有一个副本。静态变量可以实现让多个对象共享内存。

实例变量: 必须依存于某一实例,需要先创建对象然后通过对象才能访问到它。

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

类似地。

静态方法:static 修饰的方法,也被称为类方法。在外部调⽤静态⽅法时,可以使⽤"类名.⽅法名"的⽅式,也可以使⽤"对象名.⽅法名"的⽅式。静态方法里不能访问类的非静态成员变量和方法。

实例⽅法:依存于类的实例,需要使用"对象名.⽅法名"的⽅式调用;可以访问类的所有成员变量和方法

8.final 关键字有什么作用?

  • 被 final 修饰的类不可以被继承

  • 被 final 修饰的方法不可以被重写

  • 被 final 修饰的变量不可变

  • 不可变指的是变量的引用不可变,不是引用指向的内容的不可变

final修饰变量

9.final、finally、finalize 的区别?

  • final 用于修饰变量、方法和类:final 修饰的类不可被继承;修饰的方法不可被重写;修饰的变量不可变。

  • finally 作为异常处理的一部分,它只能在 try/catch 语句中,并且附带一个语句块表示这段语句最终一定被执行(无论是否抛出异常),经常被用在需要释放资源的情况下,System.exit (0) 可以阻断 finally 执行。

  • finalize 是在 java.lang.Object 里定义的方法,也就是说每一个对象都有这么个方法,这个方法在 gc 启动,该对象被回收的时候被调用。

    一个对象的 finalize 方法只会被调用一次,finalize 被调用不一定会立即回收该对象

10.==和 equals 的区别?

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

equals() : 它的作⽤也是判断两个对象是否相等。但是这个“相等”一般也分两种情况:

  • 默认情况:类没有覆盖 equals() ⽅法。则通过 equals() 比较该类的两个对象时,等价于通过“ == ”比较这两个对象,还是相当于比较内存地址

  • 自定义情况:类覆盖了 equals() ⽅法。我们平时覆盖的 equals()方法一般是比较两个对象的内容是否相同,自定义了一个相等的标准,也就是两个对象的值是否相等。

11.hashCode 与 equals?

面试常问——“你重写过 hashcode 和 equals 么,为什么重写 equals 时必须重写 hashCode ⽅法?”

什么是 HashCode?

hashCode() 的作⽤是获取哈希码(散列码)它实际上是返回⼀个 int 整数,定义在 Object 类中,这个⽅法通常⽤来将对象的内存地址转换为整数之后返回。

public native int hashCode();

哈希码主要在哈希表这类集合映射的时候用到,哈希表存储的是键值对(key-value),它的特点是:能根据“键”快速的映射到对应的“值”。这其中就利⽤到了哈希码!

为什么要有 hashCode?

例如 HashMap 怎么把 key 映射到对应的 value 上呢?用的就是哈希取余法,也就是拿哈希码和存储元素的数组的长度取余,获取 key 对应的 value 所在的下标位置。

为什么重写 quals 时必须重写 hashCode ⽅法?

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

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

为什么两个对象有相同的 hashcode 值,它们也不⼀定是相等的?

因为可能会碰撞, hashCode() 所使⽤的散列算法也许刚好会让多个对象传回相同的散列值。越糟糕的散列算法越容易碰撞,但这也与数据值域分布的特性有关(所谓碰撞也就是指的是不同的对象得到相同的 hashCode )。

12.是值传递,还是引用传递?

Java 语言是值传递(当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的属性可以在被调用过程中被改变,但对对象引用的改变是不会影响到调用者的。)

JVM 的内存分为堆和栈,其中栈中存储了基本数据类型和引用数据类型实例的地址,也就是对象地址。

而对象所占的空间是在堆中开辟的,所以传递的时候可以理解为把变量存储的对象地址给传递过去,因此引用类型也是值传递。

Java引用数据值传递示意图

13.深拷贝和浅拷贝?(怎么实现)

  • 浅拷贝:仅拷贝被拷贝对象的成员变量的值,也就是基本数据类型变量的值,和引用数据类型变量的地址值,而对于引用类型变量指向的堆中的对象不会拷贝。

  • 深拷贝:完全拷贝一个对象,拷贝被拷贝对象的成员变量的值,堆中的对象也会拷贝一份。

例如现在有一个 order 对象,里面有一个 products 列表,它的浅拷贝和深拷贝的示意图:

浅拷贝和深拷贝示意图

因此深拷贝是安全的,浅拷贝的话如果有引用类型,那么拷贝后对象,引用类型变量修改,会影响原对象。

浅拷贝如何实现呢?

Object 类提供的 clone()方法可以非常简单地实现对象的浅拷贝。

深拷贝如何实现呢?

  • 重写克隆方法:重写克隆方法,引用类型变量单独克隆,这里可能会涉及多层递归。

  • 序列化:可以先将原对象序列化,再反序列化成拷贝对象。

14.创建对象4方法

Java创建对象的四种方式

  • new 创建新对象

  • 通过反射机制

  • 采用 clone 机制 (注意浅拷贝和深拷贝的区别

  • 通过序列化机制(实现原理?

三,String

0.String 引用类型,不可继承

1.String 和 StringBuilder、StringBuffer 的区别?

  • String:String 的值被创建后不能修改,任何对 String 的修改都会引发新的 String 对象的生成。

  • StringBuffer:跟 String 类似,但是值可以被修改,使用 synchronized 来保证线程安全。

  • StringBuilder:StringBuffer 的非线程安全版本,性能上更高一些

3.String str1 = new String("abc")和 String str2 = "abc" 和 区别?

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

String str1 = new String("abc") 还会通过 new String() 在堆里创建一个 "abc" 字符串对象实例。所以后者可以理解为被前者包含

String s = new String("abc")创建了几个对象?

一个或两个。如果字符串常量池已经有“abc”,则是一个;否则,两个。

堆与常量池中的String

4.字符串拼接是如何实现的?

String 的确是不可变的,“+”的拼接操作,其实是会生成新的对象。

**Java8 时**JDK 对“+”号拼接进行了优化,上面所写的拼接方式会被优化为基于 StringBuilder 的 append 方法进行处理。Java 会在编译期对“+”号进行处理

5.intern 方法

  • 如果当前字符串内容存在于字符串常量池(即 equals()方法为 true,也就是内容一样),直接返回字符串常量池中的字符串

  • 否则,将此 String 对象添加到池中,并返回 String 对象的引用

四,Integer?

1.Integer a= 127,Integer b = 127;Integer c= 128,Integer d = 128;,相等吗?

答案是 a 和 b 相等,c 和 d 不相等。

  • 对于基本数据类型==比较的值

  • 对于引用数据类型==比较的是地址

Integer a= 127 这种赋值,是用到了 Integer 自动装箱的机制。自动装箱的时候会去缓存池里取 Integer 对象,没有取到才会创建新的对象。

如果整型字面量的值在-128 到 127 之间,那么自动装箱时不会 new 新的 Integer 对象,而是直接引用缓存池中的 Integer 对象,超过范围 a1==b1 的结果是 false

2.String 怎么转成 Integer 的?原理?

PS:这道题印象中在一些面经中出场过几次。

String 转成 Integer,主要有两个方法:

  • Integer.parseInt(String s)

  • Integer.valueOf(String s)

不管哪一种,最终还是会调用 Integer 类内中的parseInt(String s, int radix)方法。

五,Object

常见方法

Object类的方法

对象比较

  • public native int hashCode() :native 方法,用于返回对象的哈希码,主要使用在哈希表中,比如 JDK 中的 HashMap。

  • public boolean equals(Object obj):用于比较 2 个对象的内存地址是否相等,String 类对该方法进行了重写用户比较字符串的值是否相等。

对象拷贝

  • protected native Object clone() throws CloneNotSupportedException:naitive 方法,用于创建并返回当前对象的一份拷贝。一般情况下,对于任何对象 x,表达式 x.clone() != x 为 true,x.clone().getClass() == x.getClass() 为 true。Object 本身没有实现 Cloneable 接口,所以不重写 clone 方法并且进行调用的话会发生 CloneNotSupportedException 异常。

对象转字符串:

  • public String toString():返回类的名字@实例的哈希码的 16 进制的字符串。建议 Object 所有的子类都重写这个方法。

多线程调度:

  • public final native void notify():native 方法,并且不能重写。唤醒一个在此对象监视器上等待的线程(监视器相当于就是锁的概念)。如果有多个线程在等待只会任意唤醒一个。

  • public final native void notifyAll():native 方法,并且不能重写。跟 notify 一样,唯一的区别就是会唤醒在此对象监视器上等待的所有线程,而不是一个线程。

  • public final native void wait(long timeout) throws InterruptedException:native 方法,并且不能重写。暂停线程的执行。注意:sleep 方法没有释放锁,而 wait 方法释放了锁 。timeout 是等待时间。

  • public final void wait(long timeout, int nanos) throws InterruptedException:多了 nanos 参数,这个参数表示额外时间(以毫微秒为单位,范围是 0-999999)。 所以超时的时间还需要加上 nanos 毫秒。

  • public final void wait() throws InterruptedException:跟之前的 2 个 wait 方法一样,只不过该方法一直等待,没有超时时间这个概念

反射:

  • public final native Class<?> getClass():native 方法,用于返回当前运行时对象的 Class 对象,使用了 final 关键字修饰,故不允许子类重写。

垃圾回收:

  • protected void finalize() throws Throwable :通知垃圾收集器回收对象。

六,异常

Java异常体系

Throwable是 Java 语言中所有错误或异常的基类。 Throwable 又分为ErrorException,其中 Error 是系统内部错误,比如虚拟机异常,是程序无法处理的。Exception是程序问题导致的异常,又分为两种:

  • CheckedException 受检异常:编译器会强制检查并要求处理的异常。

  • RuntimeException 运行时异常:程序运行中出现异常,比如我们熟悉的空指针、数组下标越界等等

处理方法

异常处理

抛出异常有三种形式,一是 throw,一个 throws,还有一种系统自动抛异常。

throws 用在方法上,后面跟的是异常类,可以跟多个;而 throw 用在方法内,后面跟的是异常对象。

  • try catch 捕获异常

在 catch 语句块中补货发生的异常,并进行处理。

       try {
            //包含可能会出现异常的代码以及声明异常的方法
        }catch(Exception e) {
            //捕获异常并进行处理
        }finally {                                                       }
            //可选,必执行的代码
        }

try-catch 捕获异常的时候还可以选择加上 finally 语句块,finally 语句块不管程序是否正常执行,最终它都会必然执行。

#41.三道经典异常处理代码题

题目 1

public class TryDemo {
    public static void main(String[] args) {
        System.out.println(test());
    }
    public static int test() {
        try {
            return 1;
        } catch (Exception e) {
            return 2;
        } finally {
            System.out.print("3");
        }
    }
}

执行结果:31。

try、catch。finally 的基础用法,在 return 前会先执行 finally 语句块,所以是先输出 finally 里的 3,再输出 return 的 1。

题目 2

public class TryDemo {
    public static void main(String[] args) {
        System.out.println(test1());
    }
    public static int test1() {
        try {
            return 2;
        } finally {
            return 3;
        }
    }
}

执行结果:3。

try 返回前先执行 finally,结果 finally 里不按套路出牌,直接 return 了,自然也就走不到 try 里面的 return 了。

finally 里面使用 return 仅存在于面试题中,实际开发不可以

题目 3

public class TryDemo {
    public static void main(String[] args) {
        System.out.println(test1());
    }
    public static int test1() {
        int i = 0;
        try {
            i = 2;
            return i;
        } finally {
            i = 3;
        }
    }
}

执行结果:2。

大家可能会以为结果应该是 3,因为在 return 前会执行 finally,而 i 在 finally 中被修改为 3 了,那最终返回 i 不是应该为 3 吗?

但其实,在执行 finally 之前,JVM 会先将 i 的结果暂存起来,然后 finally 执行完毕后,会返回之前暂存的结果,而不是返回 i,所以即使 i 已经被修改为 3,最终返回的还是之前暂存起来的结果 2。

七,IO

分类

  • 按照流的流向分,可以分为输入流输出流

  • 按照操作单元划分,可以划分为字节流字符流

  • 按照流的角色划分为节点流处理流

  • InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。

  • OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。

IO-操作方式分类-图片来源参考[2]

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

字符流是由 Java 虚拟机将字节转换得到的。过程还比较耗时,并且,如果我们不知道编码类型就很容易出现乱码问题。 I/O 流就干脆提供了一个直接操作字符的接口,方便我们平时对字符进行流操作

BIO、NIO、AIO?

八, 序列化

什么是序列化?什么是反序列化?

什么是序列化,序列化就是把 Java 对象转为二进制流,方便存储和传输。

所以反序列化就是把二进制流恢复成对象

序列化和反序列化

Serializable 接口有什么用?

这个接口只是一个标记,没有具体的作用,但是如果不实现这个接口,在有些序列化场景会报错

serialVersionUID 又有什么用?

serialVersionUID 就是起验证作用。

private static final long serialVersionUID = 1L;

我们经常会看到这样的代码,这个 ID 其实就是用来验证序列化的对象和反序列化对应的对象 ID 是否一致。

Java 序列化不包含静态变量?

序列化的时候是不包含静态变量的。

如果有些变量不想序列化,怎么办?

对于不想进行序列化的变量,使用transient关键字修饰。(只能修饰变量,不能修饰类,方法)

三种序列化

Java常见序列化方式

  • Java 对象序列化 :Java 原生序列化方法即通过 Java 原生流(InputStream 和 OutputStream 之间的转化)的方式进行转化,一般是对象输出流 ObjectOutputStream和对象输入流ObjectInputStream

  • Json 序列化:这个可能是我们最常用的序列化方式,Json 序列化的选择很多,一般会使用 jackson 包,通过 ObjectMapper 类来进行一些操作,比如将对象转化为 byte 数组或者将 json 串转化为对象。

  • ProtoBuff 序列化:ProtocolBuffer 是一种轻便高效的结构化数据存储格式,ProtoBuff 序列化对象可以很大程度上将其压缩,提高效率

九,泛型?

泛型:泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型

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.泛型类

//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
//在实例化泛型类时,必须指定T的具体类型
public class Generic<T>{
​
    private T key;
​
    public Generic(T key) {
        this.key = key;
    }
​
    public T getKey(){
        return key;
    }
}

如何实例化泛型类:

Generic<Integer> genericInteger = new Generic<Integer>(123456);

2.泛型接口

public interface Generator<T> {
    public T method();
}

实现泛型接口,指定类型:

class GeneratorImpl<T> implements Generator<String>{
    @Override
    public String method() {
        return "hello";
    }
}

3.泛型方法

   public static < E > void printArray( E[] inputArray )
   {
         for ( E element : inputArray ){
            System.out.printf( "%s ", element );
         }
         System.out.println();
    }

使用:

// 创建不同类型数组: Integer, Double 和 Character
Integer[] intArray = { 1, 2, 3 };
String[] stringArray = { "Hello", "World" };
printArray( intArray  );
printArray( stringArray  );

常用的通配符为: T,E,K,V,?

  • ? 表示不确定的 java 类型

  • T (type) 表示具体的一个 java 类型

  • K V (key value) 分别代表 java 键值中的 Key Value

  • E (element) 代表 Element

什么是泛型擦除?

所谓的泛型擦除,官方名叫“类型擦除”。

Java 的泛型是伪泛型,这是因为 Java 在编译期间,所有的类型信息都会被擦掉。

也就是说,在运行的时候是没有泛型的。

例如这段代码,往一群猫里放条狗:

LinkedList<Cat> cats = new LinkedList<Cat>();
LinkedList list = cats;  // 注意我在这里把范型去掉了,但是list和cats是同一个链表!
list.add(new Dog());  // 完全没问题!

因为 Java 的范型只存在于源码里,编译的时候给你静态地检查一下范型类型是否正确,而到了运行时就不检查了。上面这段代码在 JRE(Java运行环境)看来和下面这段没区别:

LinkedList cats = new LinkedList();  // 注意:没有范型!
LinkedList list = cats;
list.add(new Dog());

为什么要类型擦除呢?

主要是为了向下兼容,因为 JDK5 之前是没有泛型的,为了让 JVM 保持向下兼容

十, 注解

Java 注解本质上是一个标记

可以标记在类上、方法上、属性上等,标记自身也可以设置一些值

可以在编译或者运行阶段去识别这些标记,然后搞一些事情,这就是注解的用处。

常见的 AOP,使用注解作为切点就是运行期注解的应用;比如 lombok,就是注解在编译期的运行。

注解生命周期有三大类,分别是:

  • RetentionPolicy.SOURCE:给编译器用的,不会写入 class 文件

  • RetentionPolicy.CLASS:会写入 class 文件,在类加载阶段丢弃,也就是运行的时候就没这个信息了

  • RetentionPolicy.RUNTIME:会写入 class 文件,永久保存,可以通过反射获取注解信息

十一,反射

什么是反射?

我们通常都是利用new方式来创建对象实例,这可以说就是一种“正射”,这种方式在编译时候就确定了类型信息。

而如果,我们想在时候动态地获取类信息、创建类实例、调用类方法这时候就要用到反射

通过反射你可以获取任意一个类的所有属性和方法,你还可以调用这些方法和属性。

Java反射相关类

反射的应用场景?

像 Spring/Spring Boot、MyBatis 等等框架中都大量使用了反射机制。

十二,API

Math

静态方法可以 类名.方法 调用。

成员方法:成员方法:

public static int abs(int a) a的绝对值

public static double ceil(double a) a向上取整,返回double值

public static double floor(double a) a向下取整,返回double值

public static int max(int a,int b) 求a,b的最大值

public static double pow(double a,double b) a的b次方

public static double random() 生成一个0和1之间的随机返回double值

public static int round(float a) 四舍五入,int值

Math.sqrt(9)) 平方根

String?

字符串

构造方法
        /*
            String类中的构造方法
            String()   无参构造
            String(String s)  有一个 String类型参数
            String(byte[] b)  把一个byte数组转为字符串
            String(char[] c)  把一个char类型数组转为字符串
            String s = "abc"
         */
        String s=new String();
        String s1=new String("abc");
        byte[] bytes="abc".getBytes();
        System.out.println(Arrays.toString(bytes));
        //编码 :把字符串转为byte数组  数字化传输
        String s2=new String(bytes);
        System.out.println(s2);
        //解码 :把byte数组转为字符串
 
        //getBytes()转为byte数组
        //toCharArray()转为char数组
 
        char[] chars="cba".toCharArray();
        Arrays.sort(chars);
        String s3=new String(chars);
        System.out.println(s3);
        /*
            排序
            char[] chars="cba".toCharArray();
            将字符串转为数组,以数组的形式进行操作,把char数组转为字符串
         */
        System.out.println(String.valueOf(chars));
判断功能
public static void main(String[] args) {
        /*
            boolean equals(Object obj) 重写object的方法判断内容是否相同
            boolean equalsIgnoreCase(String str) 不区分大小写比较内容
            boolean contains(String str)  是否包含某个指定的子串(连续的)
            boolean isEmpty()   判断是否为空  ""
            boolean startsWith(String prefix) 判断是否以指定子串为开头
            boolean endsWith(String suffix) 判断是否以指定子串结尾
            boolean compareTo(String anotherString)  按照字典比较字符串
         */
        String s1="abc";
        System.out.println(s1.equals("ab"));
        System.out.println(s1.equalsIgnoreCase("ABC"));
        System.out.println(s1.contains("bc"));
        System.out.println(s1.isEmpty());
        System.out.println(s1.startsWith("a"));
        System.out.println(s1.endsWith("bc"));
 
        System.out.println("a".compareTo("b"));
    }
获取功能
public static void main(String[] args) {
        /*
            int length() 获取字符串长度
            char charAt(int index)  获取指定字符串的一个字符
            int indexOf(String str) 查找字符第一次出现的位置(索引)
            int indexOf(String str,int fromIndex) 从指定位置查找字符出现位置
            String substring(int start)  截取字符串,从指定位置开始,截取到最后一位
            String substring(int start,int end)截取字符串,指定开始,指定结束
         */
        String s="abcdecfdsdfs";
        System.out.println(s.length());
 
        char a=s.charAt(2);
        System.out.println(a);//c
 
        int index=s.indexOf("c");
        int index1=s.indexOf("c",index+1);
        System.out.println(index);
        System.out.println(index1);
 
        String sub=s.substring(2);
        String sub1=s.substring(2,5);
        //sub是新字符串  s不变
        System.out.println(sub);
        System.out.println(sub1);
    }

替换功能

String replace(char old,char new) 新的替换旧的

String replace(String old,String new)用新的字符串替换指定的所有的子串

replaceAll(String regex, String replacement) regex

用新的字符串替换指定的所有的子串,使用正则表达式匹配

replaceFirst(String regex, String replacement)

用新的字串替换指定的第一个子串

*去除字符串两端的空格,中间的空格不能去除

System

标准输入,标准输出,错误输出;

访问外部定义的属性,环境变量

加载文件和库的方法

不能实例化

public static long currentTime() //返回当前时间(毫秒为单位)
public static void exit(int status) //终止java虚拟机,非0表示异常终止

Object

在继承中,当一个类没有直接使用extends关键字继承其他类时,这个类默认继承Object类,Object是整个Java类体系中最顶级的类

toString()

在输出对象时,将对象转为字符串形式,默认调用类中的toString()

如果类中没有toString(),那么会调用父类的方法,Object类中的toSrting默认输出对象内存地址需要对象中的值,

equals() ?

== 基本类型比较 比较值是否相等

对象.equals()方法 比较对象内容是否相等

引用类型比较 比较的是引用变量所指向的内存空间的地址是否相等

需要比较对象内容是否相等,重写object中equals()方法比较内容是否一致

hashcode()

如果对象的equals方法被重写,那么对象的HashCode方法也尽量重写

hashcode就是通过hash函数得来的,通俗的说,就是通过某一种算法得到的,hashcode就是在hash表中有对应的位置。

日期

Data类

public Date()
public Date(long date)
boolean after(Date when)
boolean before(Date when)
IntcompareTo(Date anotherDate)
boolean equals(Object obj)

SimpleDateFormat类

public SimpleDateFormat()
public SimpleDateFormat(String pattern)
public final String Format(Date date)
public Data parse(String source)

Calender类

包装类

把java基本数据类型和一些常见操作方法封装

构造包装类对象后不能改变包装类里的值,

Integer,Long,Double,Short,Float,Byte,Character,Boolean

Integer类

static int MAX_VALUE

static int MIN_VALUE

Integr i1 = Integer.valueOF(100);
int a =10;
String s = String.valueOF(a);
//s = 10
String s1 = "100";
Integer i = Integer.valueOF(s1);
int b = Integer.parseInt(s1);

自动装箱

Integer i1 =Integer.valueOF(100);

//装箱

Integer i2;

//自动装箱

i2 = i2.intValue()+100;

//拆箱

i2 = i2 +100;

//自动装箱

  • 8
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值