Java基础语法(持续更新中......)

发布时间:2024年01月25日

目录

1. Java历史

2. Java语言的特点(面试)

3. Java环境及其开发工具安装

3.1. Java环境安装

3.2. 开发工具安装

4. 快速开始一个java程序

5. 标识符

6. 修饰符

7. 变量

7.1. 局部变量

7.2. 成员变量(实例变量)

7.3. 类变量(静态变量)

7.4. 常量和静态变量的区别

7.5. 静态变量的线程安全性(提高)

8. 数据类型

8.1. 整型

8.2. 浮点型

8.2.1. 为什么浮点数值不适用于无法接受舍入误差的金融计算中?

8.2.2. 该如何解决?

8.3. 字符型

8.3.1. 字符集

8.3.1.1. ASCII字符集

8.3.1.2. GBK字符集

8.3.1.3. UTF-8字符集

8.4. 布尔型

8.5. 引用数据类型

9. 数据类型转换

10. 运算符

11. 数组

11.1. 数组这种数据结构的优点和缺点是什么?

12. 对象

12.1. 封装

12.2. 继承

12.2.1. 封装的优点

12.2.2. 继承的特性

12.2.3. 接口

12.2.4. 接口与类的区别

12.2.5. 抽象类和接口的区别

12.2.6. 重写(Override)与重载(Overload)

重写与重载之间的区别

12.3. 多态

目录

1. Java历史

2. Java语言的特点(面试)

3. Java环境及其开发工具安装

3.1. Java环境安装

3.2. 开发工具安装

4. 快速开始一个java程序

5. 标识符

6. 修饰符

7. 变量

7.1. 局部变量

7.2. 成员变量(实例变量)

7.3. 类变量(静态变量)

7.4. 常量和静态变量的区别

7.5. 静态变量的线程安全性(提高)

8. 数据类型

8.1. 整型

8.2. 浮点型

8.2.1. 为什么浮点数值不适用于无法接受舍入误差的金融计算中?

8.2.2. 该如何解决?

8.3. 字符型

8.3.1. 字符集

8.3.1.1. ASCII字符集

8.3.1.2. GBK字符集

8.3.1.3. UTF-8字符集

8.4. 布尔型

8.5. 引用数据类型

9. 数据类型转换

9.1. 隐式转换(自动类型提升):

9.2. 显式转换(强制类型转换)

10. 运算符

11. 数组

11.1. 数组这种数据结构的优点和缺点是什么?

12. 对象

12.1. 封装

12.2. 继承

12.2.1. 封装的优点

12.2.2. 继承的特性

12.2.3. 接口

12.2.4. 接口与类的区别

12.2.5. 抽象类和接口的区别

12.2.6. 重写(Override)与重载(Overload)

重写与重载之间的区别

12.3. 多态

13. String&StringBuffer&StringBuilder(提高)

13.1. StringBuffer 和 StringBuilder 的区别



1. Java历史

????????Java的历史可以追溯到1990年代初期,当时Sun Microsystems成立了一个名为“Green Team”的团队,旨在开发一种跨平台的编程语言。这个团队由Java之父James Gosling领导,并且在他的领导下,Java的前身Oak(橡树)于1991年诞生。

????????1995年,Java语言正式发布,并且得到了广泛的关注和接受。Java语言的主要特点是平台无关性,即可以在任何支持Java的平台上运行,无需重新编译。这个特性使得Java成为了一种非常流行的编程语言,特别是在Web开发和企业应用领域。

java语言版本发展历程:

版本

发布日期

说明

jdk beta

1995

java语言诞生

java 1.0

1996

首个java版本,包含基本的核心api和applet支持

java 1.1

1997

添加了内部类,反射,jar文件,事件模型等功能。

java 1.2

1998

引入了swing用户界面工具包,集合框架,javabeans等,增强了平台独立性。

java 1.3

2000

增加了javasound api,jndi,jpda调试工具,并改进了性能和稳定性。

java 1.4

2002

引入了nio,正则表达式,xml解析器等新特性,提高了性能和安全性。

java 5

2004

引入了泛型,注解,自动装箱/拆箱,枚举类型等新特性。

java 6

2006

引入了jdbc4.0,jax-ws,java compiler api等新特性,并优化了垃圾回收机制。

java 7

2011

引入了trywith-resources语句,二进制字面量,钻石操作符等新特性,并提升了性能和安全性。

java 8

2014

引入了lambda表达式,streamsapi,函数式接口,默认方法等新特性,并改进了集合框架和日期/时间api

java 9

2017

引入了模块化系统,jshell工具,集合工厂方法等新特性,并优化了垃圾回收机制和性能

java 10

2018

引入了局部变量类型推断,线程本地握手,堆内存分配等新特性

java 11

2018

主要增强了http客户端,启动时间,容器支持,嵌套访问控制等方面的功能,并删除了一些过时的api

java 12

2019

引入了Switch表达式,Jvm常量api等新特性

java 13

2019

引入了文本块,动态CDS、ZGC等新特性

java 14

2020

主要更新包括垃圾回收器的改进、模式匹配语法的使用、依赖封闭/接口封闭以及record紧凑语法。

java 15

2021

主要更新是关于垃圾回收器的改进,CMS垃圾回收器被剔除,ParallelScavenge + SerialOld GC 的组合模式被弃用,ZGC垃圾回收器转正,G1回收器得到优化后依然是默认的垃圾回收器

java 16

2021

主要更新是关于垃圾回收器的改进,ZGC可以将未使用的堆内存返回给操作系统,Records 的扩展,包括 sealed 和 non-sealed 标注;新增了 Vector API,用于并行计算。 服务提供者接口(SPAPI),取代了旧的类路径扩展机制;

java 17

2023

基于OpenJDK 11 的长期支持(LTS)版本;主要更新是关于垃圾回收器的改进,ZGC再次得到增强,可以更好地处理高吞吐量的场景。

2. Java语言的特点(面试)

  1. 简单性:相比于c++而言Java的语法就简单很多,没有什么头文件,指针,结构虚基类等
  2. 面向对象:能够将生活中的各个实体事物抽象成对象。 Java 语言提供类、接口和继承等面向对象的特性,为了简单起见,只支持类之间的单继承,但支持接口之间的多继承,并支持类与接口之间的实现机制(关键字为 implements)。
  3. 分布式:Java 语言支持 Internet 应用的开发,在基本的 Java 应用编程接口中有一个网络应用编程接口(java net),它提供了用于网络应用编程的类库,包括 URL、URLConnection、Socket、ServerSocket 等。Java 的 RMI(远程方法激活)机制也是开发分布式应用的重要手段。
  4. 健壮性: Java 的强类型机制、异常处理、垃圾的自动收集等是 Java 程序健壮性的重要保证。对指针的丢弃是 Java 的明智选择。Java 的安全检查机制使得 Java 更具健壮性。Java 编译器能够检测许多在其他语言中仅在运行时才能够检测出来的问题。
  5. 高性能: 与那些解释型的高级脚本语言相比,Java 的确是高性能的。事实上,Java 的运行速度随着 JIT(Just-In-Time)编译器技术的发展越来越接近于 C++。
  6. 多线程: 在 Java 语言中,线程是一种特殊的对象,它必须由 Thread 类或其子(孙)类来创建。通常有两种方法来创建线程:其一,使用型构为 Thread(Runnable) 的构造子类将一个实现了 Runnable 接口的对象包装成一个线程,其二,从 Thread 类派生出子类并重写 run 方法,使用该子类创建的对象即为线程。值得注意的是 Thread 类已经实现了 Runnable 接口,因此,任何一个线程均有它的 run 方法,而 run 方法中包含了线程所要运行的代码。线程的活动由一组方法来控制。Java 语言支持多个线程的同时执行,并提供多线程之间的同步机制(关键字为 synchronized)。
  7. 安全性: Java通常被用在网络环境中,为此,Java 提供了一个安全机制以防恶意代码的攻击。除了Java 语言具有的许多安全特性以外,Java 对通过网络下载的类具有一个安全防范机制(类 ClassLoader),如分配不同的名字空间以防替代本地的同名类、字节代码检查,并提供安全管理机制(类 SecurityManager)让 Java 应用设置安全哨兵。

3. Java环境及其开发工具安装

3.1. Java环境安装

  1. 安装地址:Java Downloads | Oracle
  2. 配置环境变量
  3. 弄出cmd,输入java -version检测

3.2. 开发工具安装

  1. 推荐idea,下载地址:Download IntelliJ IDEA – The Leading Java and Kotlin IDE
  2. 是学生用校园邮箱注册即可白嫖
  3. 也可以白嫖激活码

4. 快速开始一个java程序

  1. 创建一个名为HelloJava.java的类
  2. 输入以下代码,输出“Hello Java”
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello Java"); 
    }
}
  1. 找到该文件路径,windows系统win+r搜索cmd回车,进入该文件的目录使用cd命令

????????javac命令是将java源文件编译成字节码文件,输入完javac HelloWord.java后便会在该文件的相同目录下生成HelloWord.class文件,然后使用java命令运行该字节码文件得到输出信息。需要注意的是该java源文件的命名需要和文件里的类名相同否则会找不到主类而报错。

5. 标识符

????????Java 所有的组成部分都需要名字。类名变量名以及方法名都被称为标识符。关于 Java 标识符,有以下几点需要注意:

  1. 所有的标识符都应该以字母(A-Z 或者 a-z),美元符($)、或者下划线(_)开始
  2. 首字符之后可以是字母(A-Z 或者 a-z),美元符($)、下划线(_)或数字的任何字符组合
  3. 关键字不能用作标识符
  4. 标识符是大小写敏感的
  5. 合法标识符举例:age、$salary、_value、__1_value
  6. 非法标识符举例:123abc、-salary

6. 修饰符

访问控制修饰符:public,default,private,protected

非访问控制修饰符:final,abstract,static,synchronized

  1. static 修饰符,用来修饰类方法和类变量
  2. final 修饰符,用来修饰类、方法和变量,final 修饰的类不能够被继承,修饰的方法不能被继承类重新定义,修饰的变量为常量,是不可修改的
  3. abstract 修饰符,用来创建抽象类抽象方法
  4. synchronized 和 volatile 修饰符,主要用于线程的编程。

7. 变量

7.1. 局部变量

????????在方法构造方法或者语句块中定义的变量被称为局部变量。变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁。

7.2. 成员变量(实例变量)

????????成员变量是定义在中,方法体之外的变量。这种变量在创建对象的时候实例化。成员变量可以被类中方法、构造方法和特定类的语句块访问。 通常,实例变量应该被设为私有,以防止外部代码随意修改其值。

7.3. 类变量(静态变量)

  1. 生命周期:在类加载时被创建,在整个程序运行期间都存在,直到程序结束才会被销毁 ,静态变量可以用来存储整个程序都需要使用的数据,如配置信息、全局变量等。
  2. 声明:类变量也声明在类中,方法体之外,但必须声明为 static 类型
  3. 访问方式: 由于静态变量是与类相关的,因此可以通过类名来访问静态变量,也可以通过实例名来访问静态变量。

7.4. 常量和静态变量的区别

????????常量也是与类相关的,但它是用 final 关键字修饰的变量,一旦被赋值就不能再修改。与静态变量不同的是,常量在编译时就已经确定了它的值,而静态变量的值可以在运行时改变。

????????另外,常量通常用于存储一些固定的值,如数学常数、配置信息等,而静态变量通常用于存储可变的数据,如计数器、全局状态等。静态变量是与类相关的变量,具有唯一性和共享性,可以用于存储整个程序都需要使用的数据,但需要注意初始化时机和与常量的区别。

7.5. 静态变量的线程安全性(提高)

????????Java 中的静态变量是属于类的,而不是对象的实例。因此,当多个线程同时访问一个包含静态变量的类时,需要考虑其线程安全性。静态变量在内存中只有一份拷贝,被所有实例共享。因此,如果一个线程修改了静态变量的值,那么其他线程在访问该静态变量时也会看到修改后的值。这可能会导致并发访问的问题,因为多个线程可能同时修改静态变量,导致不确定的结果或数据一致性问题。为了确保静态变量的线程安全性,需要采取适当的同步措施,如同步机制、原子类或 volatile 关键字,以便在多线程环境中正确地读取和修改静态变量的值。

8. 数据类型

基本数据类型分为四大类,八小种

8.1. 整型

整数型

占用字节

表示范围

byte

1

-128~127

short

2

-32768~32767

int

4

-2^31~(2^31-1)

long

8

-2^63~(2^63-1)

8.2. 浮点型

浮点型

占用字节

表示范围

float

4

大约 ± 3.402 823 47E+38F (有效位数为 6 ~ 7 位)

double

8

大约 ± 1.797 693 134 862 315 70E+308 (有效位数为 15 位)

8.2.1. 为什么浮点数值不适用于无法接受舍入误差的金融计算中?

  1. 二进制表示法的限制:二进制无法精确表示某些十进制小数。例如,十进制的 0.1 在二进制中是一个无限循环小数,但计算机只能有限地存储这个数值,导致精度损失。使用 float 或 double 来表示这类小数时,可能会得到一个近似的值,而不是精确的值。
  2. 浮点运算的误差:浮点数的运算(如加、减、乘、除)可能会引入舍入误差。这些误差可能在大多数情况下不会造成问题,但在某些情况下可能会导致不可预测的结果。例如,在进行多次小数运算后,这些误差可能会累积起来,导致最终的结果与预期值产生较大的偏差。

8.2.2. 该如何解决?

????????在Java开发中,计算金额时通常使用`BigDecimal`数据类型。`BigDecimal`类提供了高精度的浮点数计算,适合用于精确计算货币金额,避免了使用基本数据类型(如`float`和`double`)可能产生的精度问题。

8.3. 字符型

了解char之前必须要弄清楚Unicode编码机制

8.3.1. 字符集

ascii字符集:只有英文,数字,符号等,占1个字节。
gbk字符集:汉字占2个字节,英文,数字占1个字节。
utf-8字符集:汉字占3个字节,英文,数字占1个字节。

????????编码和解码时需要使用同一种编码方式,英文数字一般不会出现乱码。英文很多字符集都兼容ASCII字符集

8.3.1.1. ASCII字符集

????????美国ASCII(美国信息交换标准代码)字符集,使用一个字节来存储,这个字节首位都是0,表示128个字符,对美国人够用了。

8.3.1.2. GBK字符集
  1. GBK汉字编码字符集,包含两万多个汉字和字符GBK中一个中文字符编码占2个字节
  2. GBK必须兼容ASCII字符集
  3. 汉字的第一个字节的第一位必须是1,是为了和ASCII中的字符区分开,ASCII只占一个字节且是以0为首位,目的便于解码

????????世界上编码过多,容易出现问题,此时Unicode(万国码)应运而生,一统天下。Unicode是国际组织制定的,可以容纳世界上所有的文字,符号和字符集。UTF-8是一直重要的编码方案

8.3.1.3. UTF-8字符集

汉字占三个字节,英文数字占1个字节

8.4. 布尔型

????????boolean (布尔)类型有两个值:false 和 true, 用来判定逻辑条件 整型值和布尔值之间 不能进行相互转换

8.5. 引用数据类型

????????在Java中,引用类型的变量非常类似于C/C++的指针。引用类型指向一个对象,指向对象的变量是引用变量。这些变量在声明时被指定为一个特定的类型,比如 Employee、Puppy 等。变量一旦声明后,类型就不能被改变了。

  1. 对象、数组都是引用数据类型。
  2. 所有引用类型的默认值都是null。
  3. 一个引用变量可以用来引用任何与之兼容的类型。

9. 数据类型转换

数据类型转换满足以下要求:

  1. 不能对boolean类型进行类型转换。
  2. 在把容量大的类型转换为容量小的类型时必须使用强制类型转换。
  3. 转换过程中可能导致溢出或损失精度,例如: int i =128; byte b = (byte)i;
  4. 浮点数到整数的转换是通过舍弃小数得到,而不是四舍五入,例如(int)23.7 == 23; (int)-45.89f == -45

9.1. 隐式转换(自动类型提升):

如果你尝试将一个字节小的数据类型赋值给一个字节更大的数据类型,那么会自动进行数据类型的提升。

byte -> short -> int -> long

byte -> short

short -> int

int -> long

例如,你可以这样做而不会有错误:

byte b = 10;
int i = b;

9.2. 显式转换(强制类型转换)

如果你尝试将一个字节大的数据类型赋值给一个字节小的数据类型,那么你需要进行强制类型转换,否则可能会丢失数据。

long -> int

int -> short

short -> byte

例如:

int i = 130;
byte b = (byte) i;

????????我们来分析一下为什么这样转换 int i = 130;byte b = (byte) i; 结果为 -126。首先,我们知道 byte 的范围是从 -128127。当超出这个范围时,它会进行溢出并从另一端重新开始计数。这是因为计算机中的数是基于补码进行存储的。对于正数,补码和原码是相同的。但对于负数,补码是其二进制原码的反码(取反)再加1。那么,int130 的二进制表示形式(我们只看最后8位,因为 byte 只有8位)是 10000010。当我们尝试将这个二进制数作为 byte 解释时,最高位(即最左边的位)是1,代表这是一个负数。为了知道这个数的真实值,我们需要计算其补码:

取反(取反码):01111101

加1(得到补码):01111110

这个二进制数 01111110 是十进制的 126。但由于最高位代表这是一个负数,所以实际值是 -126

因此,当你执行 byte b = (byte) 130;b 的值会变为 -126

10. 运算符

  1. 位运算符
A = 0011 1100
B = 0000 1101
-----------------
A&B = 0000 1100    // 全1则1   与运算
A | B = 0011 1101  // 有1则1   或运算
A ^ B = 0011 0001  // 不同1相同0    异或运算
~A= 1100 0011	   // 按位取反    取反运算
A<<2=1111 0000	   // 按位左移运算符。左操作数按位左移右操作数指定的位数。
A>>2=0000 1111     // 按位右移运算符。左操作数按位右移右操作数指定的位数。
  1. 逻辑运算符

  1. instanceof 运算符

????????该运算符用于操作对象实例,检查该对象是否是一个特定类型(类类型或接口类型)。instanceof运算符使用格式如下:

( Object reference variable ) instanceof  (class/interface type)

String name = "James";
boolean result = name instanceof String; // 由于 name 是 String 类型,所以返回真

11. 数组

Java 语言中提供的数组是用来存储固定大小的同类型元素。 声明语法 dataType[]arrayRefVar;

// 创建数组
dataType[] arrayRefVar = new dataType[arraySize];
dataType[] arrayRefVar = {value0, value1, ..., valuek};
// 数组的元素是通过索引访问的。数组索引从 0 开始,所以索引值从 0 到 arrayRefVar.length-1

// JDK 1.5 引进了一种新的循环类型,被称为 For-Each 循环或者加强型循环,
// 它能在不使用下标的情况下遍历数组。
// 语法格式如下:
for(type element: array)
{
    System.out.println(element);
}
  1. java中数组是一种引用数据类型.不属于基本数据类型.数组的父类是Object类
  2. 数组实际上是一个容器,可以同时容纳多个元素(数组是一个数据的集合),数组: 字面意思是"一组数据"
  3. 数组当中可以存储基本数据类型的数据,也可以存储引用数据类型的数据
  4. 数组因为是引用类型,所以数组对象是在堆内存中(数组是存储在堆当中)
  5. 数组当中如果存储的是java对象的话,实际上存储的是对象的"引用"

  1. 数组在内存方面存储的时候,数组中的元素的内存地址(存储的每一个元素都是有规则的挨着排列的),是连续的,数组实际上是一种简单的数据结构。所有的数组都是拿"第一个小方框的内存地址"作为整个数组对象的内存地址(数组中首元素的内存地址作为整个数组对象的内存地址)
  2. 数组中每一个元素都是有下标的,下标从0开始,以1递增.最后一个元素的下标是: length - 1,下标非常重要,因为我们对数组中元素进行"存取"的时候,都需要通过下标来进行.

11.1. 数组这种数据结构的优点和缺点是什么?

???? 优点:查询/查找/检索某个下标上的元素时效率极高。可以说是查询效率最高的一个数据结构。为什么检索效率高?

  1. 每一个元素的内存地址在空间存储上是连续的。
  2. 每一个元素类型相同,所以占用空间大小一样。
  3. 知道第一个元素内存地址,知道每一个元素占用空间的大小,又知道下标,所以通过一个数学表达式就可以计算出某个下标上元素的内存地址。直接通过内存地址定位元素,所以数组的检索效率是最高的。

缺点:删除和增加元素时效率低

  1. 由于为了保证数组中每个元素的内存地址连续,所以在数组上随机删除或者增加元素的时候,效率较低,因为随机增删元素会涉及到后面元素统一向前或者向后位移的操作。
  2. 数组不能存储大数据量,因为很难在内存空间上找到一块特别大的连续的内存空间。

12. 对象

12.1. 封装

????????在面向对象程式设计方法中,封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法。 封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。要访问该类的代码和数据,必须通过严格的接口控制。封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性。

12.2. 继承

12.2.1. 封装的优点

  1. 良好的封装能够减少耦合。
  2. 类内部的结构可以自由修改。
  3. 可以对成员变量进行更精确的控制。
  4. 隐藏信息,实现细节。

12.2.2. 继承的特性

  1. 子类拥有父类非 private 的属性、方法。子类中有和父类同名的属性和方法时,通过super调用,否则直接调用。
  2. 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
  3. 子类可以用自己的方式实现父类的方法。
  4. Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 B 类继承 A 类,C 类继承 B 类,所以按照关系就是 B 类是 C 类的父类,A 类是 B 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。
  5. 提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。

12.2.3. 接口

????????接口是一种完全抽象的类,它定义了一组方法(没有实现),这些方法可以被任何类实现。一个类可以实现多个接口。类描述对象的属性和方法。接口则包含类要实现的方法。除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。

????????接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。

接口可以多继承。

12.2.4. 接口与类的区别

  1. 接口不能用于实例化对象。
  2. 接口没有构造方法。
  3. 接口中所有的方法必须是抽象方法,Java 8 之后 接口中可以使用 default 关键字修饰的非抽象方法。
  4. 接口不能包含成员变量,除了 static 和 final 变量。
  5. 接口不是被类继承了,而是要被类实现。
  6. 接口支持多继承。

12.2.5. 抽象类和接口的区别

  1. 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
  2. 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
  3. 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
  4. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。

注:JDK 1.8 以后,接口里可以有静态方法和方法体了。

:JDK 1.8 以后,接口允许包含具体实现的方法,该方法称为"默认方法",默认方法使用 default 关键字修饰。更多内容可参考 Java 8 默认方法

注:JDK 1.9 以后,允许将方法定义为 private,使得某些复用的代码不会把方法暴露出去。更多内容可参考 Java 9 私有接口方法

  1. 设计层面上的区别:

????????抽象类是对一种事物的抽象,即对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。举个简单的例子,飞机和鸟是不同类的事物,但是它们都有一个共性,就是都会飞。那么在设计的时候,可以将飞机设计为一个类 Airplane,将鸟设计为一个类 Bird,但是不能将 飞行 这个特性也设计为类,因此它只是一个行为特性,并不是对一类事物的抽象描述。此时可以将 飞行 设计为一个接口Fly,包含方法fly( ),然后Airplane和Bird分别根据自己的需要实现Fly这个接口。然后至于有不同种类的飞机,比如战斗机、民用飞机等直接继承Airplane即可,对于鸟也是类似的,不同种类的鸟直接继承Bird类即可。从这里可以看出,继承是一个 "是不是"的关系,而 接口 实现则是 "有没有"的关系。如果一个类继承了某个抽象类,则子类必定是抽象类的种类,而接口实现则是有没有、具备不具备的关系,比如鸟是否能飞(或者是否具备飞行这个特点),能飞行则可以实现这个接口,不能飞行就不实现这个接口。

????????设计层面不同,抽象类作为很多子类的父类,它是一种模板式设计而接口是一种行为规范,它是一种辐射式设计。什么是模板式设计?最简单例子,大家都用过 ppt 里面的模板,如果用模板 A 设计了 ppt B 和 ppt C,ppt B 和 ppt C 公共的部分就是模板 A 了,如果它们的公共部分需要改动,则只需要改动模板 A 就可以了,不需要重新对 ppt B 和 ppt C 进行改动。而辐射式设计,比如某个电梯都装了某种报警器,一旦要更新报警器,就必须全部更新。也就是说对于抽象类,如果需要添加新的方法,可以直接在抽象类中添加具体的实现,子类可以不进行变更;而对于接口则不行,如果接口进行了变更,则所有实现这个接口的类都必须进行相应的改动。

下面看一个网上流传最广泛的例子:门和警报的例子:门都有 open()close() 两个动作,此时我们可以定义通过抽象类和接口来定义这个抽象概念:

abstract class Door {
    public abstract void open();
    public abstract void close();
}

但是现在如果我们需要门具有报警 的功能,那么该如何实现?下面提供两种思路:

  • 将这三个功能都放在抽象类里面,但是这样一来所有继承于这个抽象类的子类都具备了报警功能,但是有的门并不一定具备报警功能;
  • 将这三个功能都放在接口里面,需要用到报警功能的类就需要实现这个接口中的 open( ) 和 close( ),也许这个类根本就不具备 open( ) 和 close( ) 这两个功能,比如火灾报警器。

????????从这里可以看出, Door 的 open() 、close() 和 alarm() 根本就属于两个不同范畴内的行为,open() 和 close() 属于门本身固有的行为特性,而 alarm() 属于延伸的附加行为。因此最好的解决办法是单独将报警设计为一个接口,包含 alarm() 行为,Door 设计为单独的一个抽象类,包含 open 和 close 两种行为。再设计一个报警门继承 Door 类和实现 Alarm 接口。

interface Alram {
    void alarm();
}
 
abstract class Door {
    void open();
    void close();
}
 
class AlarmDoor extends Door implements Alarm {
    void oepn() {
      //....
    }
    void close() {
      //....
    }
    void alarm() {
      //....
    }
}

12.2.6. 重写(Override)与重载(Overload)

????????重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。重写方法不能抛出新的检查异常或者比被重写方法申明更加宽泛的异常。

重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。

每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。

最常用的地方就是构造器的重载。

重载规则:

  1. 被重载的方法必须改变参数列表(参数个数或类型不一样);
  2. 被重载的方法可以改变返回类型;
  3. 被重载的方法可以改变访问修饰符;
  4. 被重载的方法可以声明新的或更广的检查异常;
  5. 方法能够在同一个类中或者在一个子类中被重载。
  6. 无法以返回值类型作为重载函数的区分标准。
重写与重载之间的区别

12.3. 多态

对象多态,行为多态,Animal cat = new Cat( );cat.move();编译看左边,运行看右边

????????java程序永远都分为编译阶段和运行阶段。先分析编译阶段,再分析运行阶段,编译无法通过,根本是无法运行的。编译阶段编译器检查cat个引用的数据类型为animal,由于animal.class字节码当中有move()方法,所以编译通过了。这个过程我们称为静态绑定 编译阶段绑定。只有静态绑定成功之后才有后续的运行。

????????在程序运行阶段,jvm内存当中真实创建的对象是cat对象,那么以下程序在运行阶段一定会调用cat对象的move()方法,此时发生了程序的动态绑定 运行阶段绑定。无论是cat类有没有重写move方法,运行阶段一定调用的是cat对象的move方法,因为底层真实对象就是cat对象。

父类型引用指向子类型对象这种机制导致程序存在编译阶段绑定和运行阶段绑定两种不同的形态/状态,

这种机制可以成为一种多态语法机制

多态就是父类型的引用可以指向子类型的对象,程序产生了一种多种形态的概念第一种是编译期形态,第二种为运行期形态,两种不同的形态可以称为多态语法机制

  1. 向上转型:子类型 --> 父类型又被称为自动类型转换,可以用在一个方法中接收参数的统一,这样不管有多少子类,只用时接收参数为父类。向上转型只能调用父类和子类中实现重写的方法,不能调用子类独属的方法。此时需要使用到向下转型。Cat cat2=(Cat) cat; cat2.work();//子类独有方法。
  2. 向下转型(只有要访问子类中特有的方法时才需要强转!)父类型 --> 子类型又被称为:强制类型转换(要加强制类型转换符)

多态的实现方式:重写接口抽象类和抽象方法;

13. String&StringBuffer&StringBuilder(提高)

Java 中字符串属于对象,Java 提供了 String 类来创建和操作字符串。 String类有50多个方法,在后面单独开一篇讲解Java常用类及其常用方法。String 类是不可改变的,所以你一旦创建了 String 对象,那它的值就无法改变了 。

String存在于字符串常量池(StringTable)中,字符串常量池在不同版本的jdk里面存在于jvm里不同的区域。jdk1.6中,字符串常量池在方法区中,方法区的实现叫永久代,永久代存在于jvm内存。

在jdk1.8中方法区的实现叫元空间,元空间存在于本地内存。此时字符串常量池(StringTable)存在于堆里面。

相同的字符串在字符串常量池中只有一份,作用自然是为了节约内存。

当对字符串进行修改的时候,需要使用 StringBuffer 和 StringBuilder 类。 StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。

13.1. StringBuffer 和 StringBuilder 的区别

  • 线程安全性
    • StringBuffer:是线程安全的。其大部分方法都是同步的,这意味着在多线程环境中,两个或更多的线程在同时访问 StringBuffer 的实例时,不会出现数据不一致的问题。
    • StringBuilder:不是线程安全的。它没有同步的方法,因此其操作在速度上通常比 StringBuffer 快,但如果在多线程环境中需要修改字符串,应特别小心使用它,以避免出现数据不一致或其他问题。从 JDK 5 开始,StringBuffer 已经补充了一个为单线程使用而设计的等效类,
    • 线程安全性
    • StringBuffer:是线程安全的。其大部分方法都是同步的,这意味着在多线程环境中,两个或更多的线程在同时访问 StringBuffer 的实例时,不会出现数据不一致的问题。
    • StringBuilder:不是线程安全的。它没有同步的方法,因此其操作在速度上通常比 StringBuffer 快,但如果在多线程环境中需要修改字符串,应特别小心使用它,以避免出现数据不一致或其他问题。从 JDK 5 开始,StringBuffer 已经补充了一个为单线程使用而设计的等效类,StringBuilder。通常应优先使用 StringBuilder 类,因为它支持所有相同的操作,但速度更快,因为它不执行同步。
    • 性能
    • 由于 StringBuffer 的方法大部分是同步的,因此在单线程环境中,它通常比 StringBuilder 慢。而 StringBuilder,由于其方法不是同步的,因此在许多操作中表现得更快。

    • 。通常应优先使用 StringBuilder 类,因为它支持所有相同的操作,但速度更快,因为它不执行同步。
  • 性能
    • 由于 StringBuffer 的方法大部分是同步的,因此在单线程环境中,它通常比 StringBuilder 慢。而 StringBuilder,由于其方法不是同步的,因此在许多操作中表现得更快。

文章来源:https://blog.csdn.net/weixin_54282800/article/details/135736637
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。