【Java基础】Java中你必须知道的知识点

时间:2021-06-27 14:00:41

本篇博客主要记录Java中面向对象的概念知识和一些基础Java类的使用。属于自己平时学习过程中知识点的“拼凑”,方便自己回顾总结。


1. 什么是面向对象

面向对象是一种优秀的软件设计思想,是相对于面向过程、面向切面等设计思想的一种软件设计理念。它的核心思想是运用更加贴近人类思维的方式去设计软件,将软件中的各个组件抽象成相应的类,再将这些类组装成我们所需的软件系统。这里举个例子,假如用面向对象的方式设计一个电脑,我们会设计CPU类、硬盘类、显示器类、内存类等等,然后将这些类组合在一起设计成Computer类。

面向过程的思想在解决问题时会将问题分解成一个个小的函数,然后按照某种顺序去执行这些方法,当这些方法执行完毕,问题也就解决了。

2. 三大基本特征和五项基本原则

面向对象的三个基本特征是:封装、继承和多态。正是基于这些特征,面向对象的开发语言才能拥有更好的可重用性、扩展性和维护性。

  • 封装:将对象的实现细节隐藏起来,然后通过一些公共的方法向外部提供该对象的功能;
  • 继承:继承是软件复用的一种重要手段,子类继承父类之后将直接获得父类的属性和方法;
  • 多态:对象可以赋给父类对象和接口,但是运行时依然表现出子类或实现类的特征。

面向对象的五大原则如下:

  • 单一职责原则(SRP):一个类专注于实现一个功能;
  • 开闭原则(OCP):对象或实体应该对扩展开放,对修改封闭;
  • 里氏替换原则(LSP):子类可以替换父类并且出现在父类能够出现的任何地方,这就需要我们面向接口编程;
  • 依赖倒置原则(DIP):高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象;抽象不应该依赖于具体实现,具体实现应该依赖于抽象;
  • 接口隔离原则(ISP):使用多个专门的接口比使用单个接口要好的多。在实际编程中,为了减少接口的定义,将许多方法都放在一个接口中,最后发现,维护和实现接口的时候会花费很多精力。而接口所定义的操作相当于对客户端的一种承诺,这种承诺当然是越少越好,越精练越好,过多的承诺带来的就是你的大量精力和时间去维护。

3. Java的平台无关性

Java语言能跨平台的关键是JVM能跨平台。JVM还支持的语言有Kotlin、Groovy、JRuby、Jython、Scala等。

4. 值传递和引用传递

基本数据类型,保存的是数据本身的值,按值传递;引用类型的变量保存的是对象在内存中的地址,按引用传递(引用传递可以看成是一种特殊的值传递)。

5. 方法重载和重写

方法重载是指在同一个类中存在多个方法名相同,但是方法参数不用的方法;

方法重写是指子类继承父类后,重新修改了父类的某个方法的行为(子类中存在这样一个方法,这个方法的方法名方法参数都和父类中某个方法一样,但是这个两个方法的方法体不一样,这种情况我们称子类方法重写了父类方法)

6. 基本数据类型

Java中有8种基本数字类型。从大类上分的话分别是布尔型,字符型,整形和浮点型。对应bool,byte,char,short,int,long,float和double类型。

  • byte:一个字节,8个bit位,-2^7 ~ 2^7-1
  • char : 2个字节,16个bit位,0 ~ 2^16-1 ;
  • short:2个字节,16个bit位,-2^15 ~ 2^15-1
  • int:4个字节,32个bit位,-2^31 ~ 2^31-1
  • long:8个字节,64个bit位,-2^63 ~ 2^63-1
  • float:4个字节,32个bit位
  • double:8个字节,64个bit位。

在计算机中,浮点数用来近似表示任意某个实数。浮点数分为双精度浮点数和单精度浮点数。单精度占4个字节,双精度占8个字节,表示的范围更大。在要求精确计算的场合,不建议使用浮点型数据。因为浮点数计算的结果不是很精确,是近似的结果。这种情况应该使用BigDecimal。这边举个BigDecimal使用的简单列子:

BigDecimal bigDecimal1 = new BigDecimal(String.valueOf(1.1));
BigDecimal bigDecimal2 = new BigDecimal(String.valueOf(2.223));
String str1 = bigDecimal1.add(bigDecimal2).toString(); //减法
bigDecimal1 = new BigDecimal(String.valueOf(2.2));
bigDecimal2 = new BigDecimal(String.valueOf(10.1));
String str2 = bigDecimal1.subtract(bigDecimal2).toString();
//乘法
bigDecimal1 = new BigDecimal(String.valueOf(1.1));
bigDecimal2 = new BigDecimal(String.valueOf(2.234));
String str3 = bigDecimal1.multiply(bigDecimal2).toString();
//除法
bigDecimal1 = new BigDecimal(String.valueOf(4.4));
bigDecimal2 = new BigDecimal(String.valueOf(3));
//除法需要设置精度和四舍五入的方式
String str4 = bigDecimal1.divide(bigDecimal2,3, RoundingMode.HALF_UP).toString();
//结果
System.out.println("加法结果:" + Double.valueOf(str1));
System.out.println("减法结果:" + Double.valueOf(str2));
System.out.println("乘法结果:" + Double.valueOf(str3));
System.out.println("除法结果:" + Double.valueOf(str4));

7. 包装类型

包装类型是相对于基本数据类型来讲的。Java中有8种基本数据类型,每个基本数据类型都有相对的包装类型。比如int的包装类型是Integer。包装类型是引用类型,而且都是不可变类。

  • 自动装箱:将基本数据类型赋值给包装类型的过程,实现原理是编译器层面new了一个包装类再赋值给相应的变量;
  • 自动拆箱:包装类型直接赋值给基本数据类型,实现原理类似,也是编译器层面调用了包装类的getValue方法再赋值给对应的基本数值类型。

比较有趣的是,包装类型在加载的过程中都会缓存某些值的类。比如Integer会缓存-128到127的类。

//不会使用缓存的数据
Integer num1 = new Integer(1);
//会使用缓存的数据
Integer num2 = 1;

通过设置-XX:AutoBoxCacheMax=?这个参数,可以调整Integer缓存的最大值。当然其他包装类型也有类似的行为,Byte、Short和Long都缓存了-128到127的类,Character缓存了0到127的类,但是这些类不能像Integer那样修改缓存的最大值。

8. String类

String是我们开发过程中使用的最多的一个类。它是一个不可变类,不能被继承。String类常用的方法如下图。另外String还有一个format方法可以进行非常丰富的格式化功能。具体的使用方式可以参考这篇博客

【Java基础】Java中你必须知道的知识点

8.1 subString方法的原理

public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[]; /** Cache the hash code for the string */
private int hash; // Default to 0 /** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -6849794470754667710L; }

String的类别是用char数组实现的,从代码中可以看到char数组用final修饰了,所以是不可变的。调用String的subString方法其实是又重新new了一个新的String对象:

public String substring(int beginIndex, int endIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
if (endIndex > value.length) {
throw new StringIndexOutOfBoundsException(endIndex);
}
int subLen = endIndex - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
return ((beginIndex == 0) && (endIndex == value.length)) ? this
: new String(value, beginIndex, subLen);
}

8.2 repalce函数

  • replace(char oldChar, char newChar):使用newChar替换所有的oldChar,不是基于正则表达式的;
  • replace(CharSequence target, CharSequence replacement):替换所有,基于正则表达式的;
  • public String replaceFirst(String regex, String replacement):替换regex匹配的第一个字符串,基于正则表达式;
  • replaceAll(String regex, String replacement):替换regex匹配的所有字符串,基于正则表达式;

8.3 valueOf函数

String提供了很多valueOf函数,比如valueOf(int i)、valueOf(double i)等。其实背后调用的是对应包装类的toSting方法

public static String valueOf(int i) {
return Integer.toString(i);
}
//Integer的toString方法
public static String toString(int i) {
if (i == Integer.MIN_VALUE)
return "-2147483648";
int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
char[] buf = new char[size];
getChars(i, size, buf);
return new String(buf, true);
}

8.3 常量池(来源于互联网)

常量池可以分为Class常量池、运行时常量池和字符串常量池:

  1. class文件常量池

    在Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用。下面对字面量和符号引用进行说明。

字面量类似与我们平常说的常量,主要包括以下两种

  • 文本字符串,例如String a = "aa"。其中"aa"就是字面量。
  • 被final修饰的变量。

符号引用包括以下形式:

  • 类和接口和全限定名:例如对于String这个类,它的全限定名就是java/lang/String。
  • 字段的名称和描述符:所谓字段就是类或者接口中声明的变量,包括类级别变量和实例级的变量。
  • 方法的名称和描述符:所谓描述符就相当于方法的参数类型+返回值类型。
  1. 运行时常量池

    我们知道类加载器会加载对应的Class文件,而上面的class文件中的常量池,会在类加载后进入方法区中的运行时常量池【此时存在在内存中】。并且需要的注意的是,运行时常量池是全局共享的,多个类共用一个运行时常量池。并且class文件中常量池多个相同的字符串在运行时常量池只会存在一份。注意运行时常量池存在于方法区中。

  2. 字符串常量池

    看名字我们就可以知道字符串常量池会用来存放字符串,也就是说常量池中的文本字符串会在类加载时进入字符串常量池。那字符串常量池和运行时常量池是什么关系呢?上面我们说常量池中的字面量会在类加载后进入运行时常量池,其中字面量中有包括文本字符串,显然从这段文字我们可以知道字符串常量池存在于运行时常量池中。也就存在于方法区中。到了JDK1.7时,字符串常量池就被移出了方法区,转移到了堆里了。那么我们可以推断,到了JDK1.7以及之后的版本中,运行时常量池并没有包含字符串常量池,运行时常量池存在于方法区中,而字符串常量池存在于堆中。

程序运行时,除非手动向常量池中添加常量(比如调用intern方法),否则jvm不会自动添加常量到常量池。

8.4 intren方法

intren方法的作用是检测常量池中是否有当前字符串,有的话就返回常量池中的对像,没有的话就将当前对像放入常量池。

8.5 兄弟类之间的比较

【Java基础】Java中你必须知道的知识点

公众号推荐

欢迎大家关注我的微信公众号「程序员*之路」

【Java基础】Java中你必须知道的知识点