1. Java介绍
Java是一个面向对象的编译型语言。其具有可移植性、安全性和健壮性的特点。同时还具备多线程和网络编程。
- 可移植性,由于其语言特点Java会在将代码编译为二进制文件后在JVM虚拟机中执行,或者jit(即时编译)将热点代码转为机器码本地执行,这就意味着通过jvm Java有着较高的可移植性
- 安全性,Java有着严格的安全机制包括字节码校验、安全管理等
- 健壮性,Java 有着强大的错误处理和异常处理机制,当程序出现错误时能够及时捕获并处理异常避免程序崩溃。
2. 基础语法
2.1. 标识符
标识符: 用来标识类名、对象名、变量名、方法名、类型名、数组名、文件名的有效字符序列。
标识符命名规则:
- 由字母、数字、下划线“_”、美元符号“$”或者“¥”组成,并且首字符不能是数字。
- 不能把Java关键字和保留字作为标识符。
- 标识符对大小写敏感。
2.2. 变量
变量:指在程序运行期间可以被更改的值,在使用变量前需要对变量进行声明,可以理解为申请一个容器,在程序运行过程中可以对容器中的内容进行取放更改。
// 声明
数据类型 变量名;
// 赋值
数据类型 = 值;
//或者
数据类型 变量名 = 值;
int age = 23;
2.3. 注释
注释可以分为单行注释、多行注释和文档注释
// 这是单行注释
/*
这是多行注释
*/
/**
* 这是一个示例类,用于演示 Java 注释的用法。
*
* @author 你的名字
* @version 1.0
*/
2.4. 访问修饰符
在 Java 中,访问修饰符用于控制类、方法和变量的访问范围。主要有以下四种访问修饰符:
- public
- 被 public 修饰的成员可以被任何其他类访问,无论它们是否在同一个包中。
public class MyClass {
public int publicVariable;
public void publicMethod() {
System.out.println("This is a public method.");
}
}
- private
- 被 private 修饰的成员只能在其所属的类内部被访问。
public class MyClass {
private int privateVariable;
private void privateMethod() {
System.out.println("This is a private method.");
}
}
- protected
- 被 protected 修饰的成员可以在同一个包中的任何类以及不同包中的子类中访问。
public class MyClass {
protected int protectedVariable;
protected void protectedMethod() {
System.out.println("This is a protected method.");
}
}
- 默认
- 当没有显式指定访问修饰符时,成员具有默认的访问权限。默认访问权限的成员可以在同一个包中的任何类中访问,但不能在不同包中的类中访问。
3. 基本数据类型
3.1. 整数类型
byte
:字节型,占 1 个字节(8 位),取值范围是 -128 到 127。short
:短整型,占 2 个字节(16 位),取值范围是 -32768 到 32767。int
:整型,占 4 个字节(32 位),取值范围是 -2147483648 到 2147483647。long
:长整型,占 8 个字节(64 位),取值范围非常大,可表示很大的整数。
3.2. 浮点类型
float
:单精度浮点型,占 4 个字节,可表示带有小数部分的数字,精度相对较低。double
:双精度浮点型,占 8 个字节,精度比float
高,通常用于需要更高精度的计算。
3.3. 字符类型
char
:占 2 个字节,用于表示单个字符,可以是字母、数字、符号等。例如,'A'、'9'、'#' 等。
3.4. 布尔类型
boolean
:占 1 个字节或 4 个字节(具体取决于 JVM 实现),只有两个可能的值:true
和false
,通常用于条件判断和逻辑运算。
3.5. 数据类型转换
- 自动转换,自动类型转换是指在满足一定条件下,Java 自动将一种数据类型转换为另一种数据类型。这种转换通常是从低精度的数据类型向高精度的数据类型转换。
- 当把一个取值范围小的类型赋值给一个取值范围大的类型时,会自动进行类型转换。例如,将一个
byte
类型的值赋给一个int
类型的变量,或者将一个int
类型的值赋给一个long
类型的变量等。 - 具体的转换顺序为:
byte
→short
→int
→long
→float
→double
。
- 强制转换,强制类型转换是指在不满足自动类型转换条件时,通过显式地指定目标数据类型来进行的类型转换。这种转换可能会导致数据丢失或精度降低。
- 当把一个取值范围大的类型赋值给一个取值范围小的类型时,需要进行强制类型转换。例如,将一个
double
类型的值赋给一个int
类型的变量,或者将一个long
类型的值赋给一个short
类型的变量等。 - 强制类型转换的语法为:(目标数据类型) 表达式。
double d = 10.5;
int i = (int)d;
// 强制将 double 类型转换为 int 类型,此时小数部分被截断,i 的值为 10
需要注意的是,在进行强制类型转换时,要确保转换是合理的,避免出现数据丢失或错误的结果。同时,对于一些复杂的数据类型,如对象类型的转换,需要满足特定的条件和方法调用,不能简单地进行强制类型转换。
4. 运算符
运算符类型 |
运算符 |
描述 |
示例 |
算术运算符 |
+ |
加法、字符串连接 |
5 + 3 = 8;"Hello" + "World" = "HelloWorld" |
算术运算符 |
- |
减法 |
8 - 3 = 5 |
算术运算符 |
* |
乘法 |
4 * 3 = 12 |
算术运算符 |
/ |
除法(整数除法结果取整,浮点数除法正常) |
10 / 3 = 3(整数);10.0 / 3 = 3.3333333333333335(浮点数) |
算术运算符 |
% |
取余 |
10 % 3 = 1 |
赋值运算符 |
= |
简单赋值 |
int a = 5; |
赋值运算符 |
+= |
-= |
*= |
比较运算符 |
== |
等于 |
5 == 5 的结果是 true |
比较运算符 |
!= |
不等于 |
5!= 3 的结果是 true |
比较运算符 |
> |
大于 |
5 > 3 的结果是 true |
比较运算符 |
< |
小于 |
3 < 5 的结果是 true |
比较运算符 |
>= |
大于等于 |
5 >= 5 的结果是 true |
比较运算符 |
<= |
小于等于 |
3 <= 5 的结果是 true |
逻辑运算符 |
&& |
逻辑与 |
true && false 的结果是 false |
逻辑运算符 |
|| |
逻辑或 |
true |
逻辑运算符 |
! |
逻辑非 |
!true 的结果是 false |
位运算符 |
& |
按位与 |
5 & 3 的结果是 1(二进制 0101 & 0011 = 0001) |
位运算符 |
| |
按位或 |
5 |
位运算符 |
^ |
按位异或 |
5 ^ 3 的结果是 6(二进制 0101 ^ 0011 = 0110) |
位运算符 |
~ |
按位取反 |
~5 的结果是 -6(二进制 ~0101 = 1010,补码表示为 -6) |
位运算符 |
<< |
左移 |
5 << 2 的结果是 20(二进制 0101 << 2 = 10100) |
位运算符 |
>> |
右移 |
5 >> 1 的结果是 2(二进制 0101 >> 1 = 0010) |
位运算符 |
>>> |
无符号右移 |
与右移运算符类似,但无论符号位是 0 还是 1,都用 0 填充高位 |
条件运算符 |
?: |
三元运算符 |
int a = (5 > 3)? 10 : 20; 如果 5 > 3 为 true,则 a 的值为 10,否则为 20 |
逗号运算符 |
, |
在一条语句中分隔多个表达式 |
int a = 5, b = 10; 同时声明并初始化两个变量 |
instanceof 运算符 |
instanceof |
判断对象是否是某个类或接口的实例 |
Object obj = new String(); boolean isString = obj instanceof String; 如果 obj 是 String 类的实例,则 isString 的值为 true |
5. 流程控制语句
5.1. 条件判断
- if 语句
if (condition) {
// 如果条件为真执行的代码
}
- if else 语句
if (condition) {
// 如果条件为真执行的代码
} else {
// 如果条件为假执行的代码
}
- if else if 语句
if (condition1) {
// 如果条件 1 为真执行的代码
} else if (condition2) {
// 如果条件 2 为真执行的代码
} else {
// 如果所有条件都为假执行的代码
}
5.2. 循环语句
- while循环
while (condition) {
// 只要条件为真就会重复执行的代码
}
- do while循环
do {
// 至少会执行一次的代码
} while (condition);
- for 循环
for (initialization; condition; update) {
// 循环体代码
}
//initialization用于初始化循环变量
// condition是循环继续的条件
// update用于在每次循环后更新循环变量
- 增强 for 循环(foreach)
for (type element : array) {
// 针对数组或集合中每个元素执行的代码
}
int[] numbers = {1, 2, 3, 4, 5};
for (int number : numbers) {
System.out.println(number);
}
5.3. 跳转语句
- break
作用:用于跳出循环或switch
语句。
for (int m = 0; m < 10; m++) {
if (m == 5) {
break;
}
System.out.println(m);
}
- continue
作用:用于跳过当前循环的剩余部分,直接进入下一次循环。
for (int n = 0; n < 10; n++) {
if (n % 2 == 0) {
continue;
}
System.out.println(n);
}
6. 数组
在 Java 中,数组是一种数据结构,用于存储相同类型元素的固定大小的顺序集合
数组特点:
- 固定大小
- 一旦数组被创建,其大小就不能改变。如果需要存储更多或更少的元素,需要创建一个新的数组。
- 相同数据类型
- 数组中的元素必须是相同的数据类型。例如,一个整数数组只能存储整数,不能存储其他类型的数据。
- 顺序存储
- 数组中的元素是按照顺序存储在内存中的,可以通过索引来访问特定的元素。
6.1. 数组声明与初始化
- 语法:
dataType[] arrayName;
(例如:int[] numbers;
声明了一个整数类型的数组名为numbers
)。 - 也可以使用另一种语法:
dataType arrayName[];
(例如:int numbers[];
,但前一种语法更为常用)。
// 静态初始化
int[] numbers = {1, 2, 3, 4, 5};
// 动态初始化
int[] array = new int[5];
array[0] = 10;
array[1] = 20;
// 以此类推
6.2. 访问数组
- 通过索引访问
- 数组的索引从 0 开始。例如,对于数组
int[] numbers = {1, 2, 3, 4, 5};
,可以使用numbers[0]
来访问第一个元素 1,numbers[1]
访问第二个元素 2,以此类推。
for (int i = 0; i < numbers.length; i++) {
System.out.println(numbers[i]);
}
- 遍历数组
- 使用 for 循环遍历数组是一种常见的方法。
for (int number : numbers) {
System.out.println(number);
}
6.3. 数组常用方法
length
属性
- 用于获取数组的长度。例如,
numbers.length
可以返回数组numbers
的长度。
Arrays
类
Java.util.Arrays
类提供了一些用于操作数组的方法,如排序、比较等。Arrays.sort()
:对数组进行排序。
int[] arrayToSort = {5, 3, 1, 4, 2};
Arrays.sort(arrayToSort);
for (int num : arrayToSort) {
System.out.println(num);
}
Arrays.equals()
:比较两个数组是否相等。
int[] array1 = {1, 2, 3};
int[] array2 = {1, 2, 3};
boolean areEqual = Arrays.equals(array1, array2);
System.out.println(areEqual);
6.4. 多维数组
- 声明、赋值
int[][] matrix = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
matrix[0][1] = 3;
- 访问
// 下标访问
System.out.println(matrix[0][1])
// 循环访问
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
System.out.print(matrix[i][j] + " ");
}
System.out.println();
}
7. Java 对象与类
在 Java 中,类(class)是一种用于创建对象的模板或蓝图,而对象则是类的具体实例。
7.1. 类定义
- 类的定义通常包括类的声明、成员变量和成员方法。
- 成员变量
-
- 成员变量是类中定义的变量,用于存储对象的状态信息。
- 可以是各种数据类型,包括基本数据类型和引用数据类型。
- 成员方法
-
- 成员方法定义了对象可以执行的操作。
- 可以接收参数并返回结果。
public class ClassName {
// 成员变量
dataType variable1;
dataType variable2;
//...
// 成员方法
returnType method1(parameterList) {
// 方法体
}
returnType method2(parameterList) {
// 方法体
}
//...
}
7.2. 对象创建
- 使用关键字
new
创建对象,语法: ClassName objectName = nwe ClassName();
public class Person {
String name;
int age;
public void sayHello() {
System.out.println("Hello, my name is " + name + " and I am " + age + " years old.");
}
}
public class Main {
public static void main(String[] args) {
Person person = new Person();
person.name = "Alice";
person.age = 30;
person.sayHello();
}
}
- 对象生命周期:对象创建时开始占用内存空间,直到不被调用被垃圾回收机制回收内存
7.3. 类与对象的关系
- 类是对象的模板
- 类定义了对象的结构和行为。
- 多个对象可以根据同一个类创建,具有相同的结构和行为。
- 对象是类的实例
- 每个对象都有自己独立的状态(成员变量的值)。
- 对象可以通过调用成员方法来执行特定的操作。
7.4. 构造器
7.4.1. 简介
在 Java 中,构造器(constructor)是一种特殊的方法,用于在创建对象时初始化对象的状态。
- 构造器的名称与类名相同,没有返回值类型,包括 void。
- 构造器的主要作用是在创建对象时为对象的成员变量赋初始值,确保对象在使用之前处于一个合理的状态。
7.4.2. 特点
- 没有返回值类型
- 构造器不能像普通方法那样有返回值类型声明,也不能使用
return
语句返回一个值。
- 与类名相同
- 构造器的名称必须与类名完全一致,这是 Java 语法的要求。
- 可以重载
- 一个类可以有多个构造器,只要它们的参数列表不同。这称为构造器重载。
public class Person {
private String name;
private int age;
public Person() {
// 默认构造器,不做任何初始化
}
public Person(String name) {
this.name = name;
this.age = 0;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
7.4.3. 无参构造器和默认构造器
- 默认构造器
- 如果一个类没有显式地定义任何构造器,Java 编译器会自动为该类生成一个默认构造器。
- 默认构造器没有参数,并且在构造对象时不做任何显式的初始化操作。
- 无参构造器
- 程序员也可以显式地定义一个无参构造器,它的作用与默认构造器类似,但可以在其中进行一些特定的初始化操作。
public class Person {
private String name;
private int age;
public Person() {
this.name = "Unknown";
this.age = 0;
}
}
7.4.4. 构造器的调用
- 在创建对象时自动调用
- 当使用
new
关键字创建一个对象时,相应的构造器会被自动调用。
- 不能在普通方法中直接调用
- 构造器只能在创建对象时由
new
关键字调用,不能像普通方法那样在其他方法中直接调用。
public class Main {
public static void main(String[] args) {
Person person1 = new Person("Alice", 30);
Person person2 = new Person("Bob");
Person person3 = new Person();
}
}
7.4.5. 构造器重载
- 提供多种初始化对象的方式
- 构造器重载允许根据不同的情况为对象提供不同的初始化方式。例如,一个表示学生的类,可以有一个构造器接受学生的姓名和年龄,另一个构造器接受学生的姓名、年龄和学号。这样,在创建学生对象时,可以根据具体的需求选择合适的构造器。
- 增强代码的灵活性和可扩展性
- 当需要为类添加新的初始化需求时,可以通过添加新的构造器来实现,而不会影响现有的代码。这使得代码更易于维护和扩展。
- 重载方法
- 参数数量不同
- 参数类型不同
- 构造器重载调用
- 当使用
new
关键字创建对象时,Java 编译器会根据提供的参数自动匹配合适的构造器进行调用。
构造器重载使得类的初始化更加灵活多样,能够满足不同场景下对象的创建需求。同时,合理地使用构造器重载可以提高代码的可读性和可维护性。
7.5. 抽象类
- 定义与特点
- 抽象类是使用
abstract
关键字修饰的类。 - 抽象类可以包含抽象方法(没有方法体,只有方法签名的方法)和具体方法。
- 抽象类不能被实例化,只能被继承。
- 作用
- 提供一个通用的模板或框架,让子类去实现具体的功能。
- 可以包含一些通用的属性和方法,减少代码重复。
abstract class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
public abstract void makeSound();
public void eat() {
System.out.println(name + " is eating.");
}
}
class Dog extends Animal {
public Dog(String name) {
super(name);
}
@Override
public void makeSound() {
System.out.println("Woof!");
}
}
7.6. 接口
- 定义与特点
- 接口使用
interface
关键字定义。 - 接口中只能包含抽象方法和常量(默认使用
public static final
修饰)。 - 类可以实现多个接口,实现接口的类必须实现接口中的所有抽象方法。
- 一个普通类可以使用
implements
关键字实现一个或多个接口。实现接口的类必须实现接口中所有的抽象方法。 - 抽象类可以实现一个或多个接口,但可以选择不实现接口中的所有方法,而是将部分或全部方法声明为抽象方法,留给子类去实现。
- 作用
- 定义一组行为规范,让不同的类实现这些规范,以达到多态的效果。
- 促进代码的解耦和可扩展性。
interface Flyable {
void fly();
}
interface Swimmable {
void swim();
}
class Duck implements Flyable, Swimmable {
@Override
public void fly() {
System.out.println("Duck is flying.");
}
@Override
public void swim() {
System.out.println("Duck is swimming.");
}
}
7.7. 封装
- 概念
-
- 封装是一种面向对象编程的原则,将数据和操作数据的方法封装在一个类中,对外提供特定的访问方式。
- 实现封装的方法
-
- 使用访问修饰符(如
private
、public
、protected
)来控制成员变量和成员方法的访问权限。 - 提供公共的方法(通常称为 getter 和 setter 方法)来访问和修改私有成员变量。
- 使用访问修饰符(如
public class Student {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
总之,在 Java 中,类是创建对象的模板,对象是类的具体实例。通过类和对象的结合,可以实现面向对象编程的封装、继承和多态等特性,提高代码的可维护性、可扩展性和可重用性。
7.8. 继承
- 继承允许一个类(子类)继承另一个类(父类)的属性和方法。
- 目的是实现代码复用,减少重复代码的编写,同时建立类之间的层次关系。
- 使用关键字 “extends” 来表示子类继承父类。
class Animal {
public void eat() {
System.out.println("Animal is eating.");
}
}
class Dog extends Animal {
public void bark() {
System.out.println("Dog is barking.");
}
}
特点
- 子类可以继承父类的非私有成员变量和方法。
- 子类可以添加自己特有的成员变量和方法。
- 子类可以重写父类的方法,以实现更具体的行为。
7.9. 多态
- 多态是指同一个行为具有多种不同的表现形式。
- 目的是提高代码的灵活性和可扩展性,使程序能够根据不同的对象类型执行不同的操作。
- 方法重载(Overloading):在同一个类中,定义多个同名方法,但参数列表不同。
class Calculator {
public int add(int a, int b) {
return a + b;
}
public int add(int a, int b, int c) {
return a + b + c;
}
}
- 方法重写(Overriding):子类重写父类的方法,以实现更具体的行为
4 class Animal {
public void makeSound() {
System.out.println("Animal makes a sound.");
}
}
class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("Meow");
}
}
- 向上转型和向下转型:通过父类引用指向子类对象实现向上转型,在需要时可以通过强制类型转换进行向下转型。
8. 输入输出
在 Java 中,输入输出(I/O)操作主要通过流(Stream)来实现。流是一组有序的数据序列,可以是输入流(用于读取数据)或输出流(用于写入数据)。
8.1. 输入流(InputStream 和 Reader)
InputStream
是字节输入流的抽象类,用于读取原始字节数据。- 常用的子类有
FileInputStream
(从文件读取数据)、ByteArrayInputStream
(从字节数组读取数据)等。
import java.io.FileInputStream;
import java.io.IOException;
public class InputStreamExample {
public static void main(String[] args) {
try (FileInputStream fis = new FileInputStream("input.txt")) {
int byteRead;
while ((byteRead = fis.read())!= -1) {
System.out.print((char) byteRead);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Reader
是字符输入流的抽象类,用于读取字符数据。- 常用的子类有
FileReader
(从文件读取字符数据)、StringReader
(从字符串读取字符数据)等。
import java.io.FileReader;
import java.io.IOException;
public class ReaderExample {
public static void main(String[] args) {
try (FileReader fr = new FileReader("input.txt")) {
int charRead;
while ((charRead = fr.read())!= -1) {
System.out.print((char) charRead);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
8.2. 输出流(OutputStream 和 Writer)
OutputStream
是字节输出流的抽象类,用于写入原始字节数据。- 常用的子类有
FileOutputStream
(向文件写入数据)、ByteArrayOutputStream
(向字节数组写入数据)等。
import java.io.FileOutputStream;
import java.io.IOException;
public class OutputStreamExample {
public static void main(String[] args) {
try (FileOutputStream fos = new FileOutputStream("output.txt")) {
String data = "Hello, World!";
fos.write(data.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
}
Writer
是字符输出流的抽象类,用于写入字符数据。- 常用的子类有
FileWriter
(向文件写入字符数据)、StringWriter
(向字符串写入字符数据)等。
import java.io.FileWriter;
import java.io.IOException;
public class WriterExample {
public static void main(String[] args) {
try (FileWriter fw = new FileWriter("output.txt")) {
String data = "Hello, World!";
fw.write(data);
} catch (IOException e) {
e.printStackTrace();
}
}
}
8.3. 缓冲流(BufferedInputStream、BufferedOutputStream、BufferedReader 和 BufferedWriter)
- 缓冲流可以提高输入输出的效率,减少对底层设备的实际读写次数。它们在内存中建立一个缓冲区,当缓冲区满或者手动刷新时才进行实际的读写操作。
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class BufferedReaderExample {
public static void main(String[] args) {
try (BufferedReader br = new BufferedReader(new FileReader("input.txt"))) {
String line;
while ((line = br.readLine())!= null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 使用缓冲流写入文件:
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class BufferedWriterExample {
public static void main(String[] args) {
try (BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"))) {
bw.write("This is a line of text.");
bw.newLine();
bw.write("Another line.");
} catch (IOException e) {
e.printStackTrace();
}
}
}
8.4. 打印流(PrintStream 和 PrintWriter)
- 打印流可以方便地将各种数据类型的数据打印到输出流中,自动进行数据类型的转换
import java.io.PrintStream;
public class PrintStreamExample {
public static void main(String[] args) {
PrintStream ps = System.out;
ps.println("Hello, World!");
ps.printf("Number: %d", 100);
}
}
- 使用打印流输出到文件:
import java.io.FileOutputStream;
import java.io.PrintStream;
public class PrintStreamToFileExample {
public static void main(String[] args) {
try (PrintStream ps = new PrintStream(new FileOutputStream("output.txt"))) {
ps.println("Hello, World!");
ps.printf("Number: %d", 100);
} catch (Exception e) {
e.printStackTrace();
}
}
}