注释
// /* … */
8种基础数据类型(primitive data type )
Integral types: byte, short, int, long
Floating-point types: float, double
The boolean data type
The char data type
变量类型
局部变量
成员变量(实例变量) - 默认权限default或声明权限public、protected、private
静态变量(类变量) - 可以通过类名访问
访问修饰符
同类访问
同包访问
位于其他包的子类访问
其他包且非子类访问
public
✅
✅
✅
✅
protected
✅
✅
✅
❌
default(无修饰)
✅
✅
❌
❌
private
✅
❌
❌
❌
default同包访问,protected同包+所有子类,public都行,private都不行
运算符
与c++同理,详情见c++笔记基础篇
Output:
1 2 3 System.out.print("Hello, " ); System.out.println("World!" ); System.out.printf("My name is %s, and I am %d years old.\n" , "Alice" , 25 );
格式符
说明
%d
整数(int, long)
%f
浮点数(float, double)
%s
字符串(String)
%c
字符(char)
%b
布尔值(boolean)
Input:
1 2 3 4 5 6 7 8 Scanner scanner = new Scanner (System.in);String name = scanner.nextLine(); int num = scanner.nextInt(); double number = scanner.nextDouble(); char ch = scanner.next().charAt(0 ); scanner.close()
条件语句
if … else if … else …
(a > b) ? a : b
switch … case … default … 其中deault非必须
||和&&
如果case中没有break也会出现“贯穿”(fall-through)
循环语句
控制关键字:
Java5引入遍历数组的增强for:
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Test { public static void main (String[] args) { int [] numbers = {10 , 20 , 30 , 40 , 50 }; for (int x : numbers ){ System.out.print( x ); System.out.print("," ); } System.out.print("\n" ); String [] names ={"James" , "Larry" , "Tom" , "Lacy" }; for ( String name : names ) { System.out.print( name ); System.out.print("," ); } } }
Java Number & Math 类
Number和Math类属于java.lang包,无需import
Java Number 类
所有的包装类(Integer、Long、Byte、Double、Float、Short)都是抽象类 Number 的子类。
包装类
基本数据类型
Boolean
boolean
Byte
byte
Short
short
Integer
int
Long
long
Character
char
Float
float
Double
double
这种由编译器特别支持的包装称为装箱,所以当内置数据类型被当作对象使用的时候,编译器会把内置类型装箱为包装类。相似的,编译器也可以把一个对象拆箱为内置类型。
方法
返回类型
说明
类型转换方法
byteValue()
byte
以 byte 类型返回数值
shortValue()
short
以 short 类型返回数值
intValue()
int
以 int 类型返回数值
longValue()
long
以 long 类型返回数值
floatValue()
float
以 float 类型返回数值
doubleValue()
double
以 double 类型返回数值
其他方法
toString()
String
返回数值的字符串形式(继承自 Object)
equals(Object obj)
boolean
判断两个 Number 对象是否相等(继承自 Object)
hashCode()
int
返回 Number 对象的哈希码(继承自 Object)
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class NumberExample { public static void main (String[] args) { Integer intObj = 100 ; Double doubleObj = 99.99 ; Float floatObj = 12.5f ; Long longObj = 100000L ; System.out.println("Integer 转换为 double: " + intObj.doubleValue()); System.out.println("Double 转换为 int: " + doubleObj.intValue()); System.out.println("Float 转换为 long: " + floatObj.longValue()); System.out.println("Integer 哈希值: " + intObj.hashCode()); System.out.println("Double 哈希值: " + doubleObj.hashCode()); System.out.println("intObj 和 longObj 是否相等: " + intObj.equals(longObj)); System.out.println("doubleObj 和 new Double(99.99) 是否相等: " + doubleObj.equals(new Double (99.99 ))); } }
Java Math 类
Java 的 Math 包含了用于执行基本数学运算的属性和方法,如初等指数、对数、平方根和三角函数。
Math 的方法都被定义为 static 形式,通过 Math 类可以在主函数中直接调用。
方法
说明
取整操作
Math.ceil(double a)
返回大于等于 a 的最小整数(向上取整)
Math.floor(double a)
返回小于等于 a 的最大整数(向下取整)
Math.round(float a)
四舍五入,返回 long 或 int
Math.abs(x)
取绝对值,x 可为 int、long、float、double
最大值/最小值
Math.max(x, y)
返回 x 和 y 中的较大值
Math.min(x, y)
返回 x 和 y 中的较小值
幂运算与根号
Math.pow(a, b)
计算 a^b(a 的 b 次幂)
Math.sqrt(x)
计算 √x(平方根)
Math.cbrt(x)
计算 ³√x(立方根)
指数与对数
Math.exp(x)
计算 e^x(自然指数函数)
Math.log(x)
计算 ln(x)(自然对数)
Math.log10(x)
计算 log₁₀(x)(以 10 为底的对数)
三角函数
Math.sin(x)
计算 x 的正弦(x 以弧度为单位)
Math.cos(x)
计算 x 的余弦
Math.tan(x)
计算 x 的正切
Math.asin(x)
计算 x 的反正弦
Math.acos(x)
计算 x 的反余弦
Math.atan(x)
计算 x 的反正切
角度与弧度转换
Math.toRadians(deg)
角度转弧度
Math.toDegrees(rad)
弧度转角度
随机数
Math.random()
返回 [0.0, 1.0) 范围的随机数
round()方法的算法为Math.floor(x+0.5),遇到负数如Math.round(-11.5)的结果为 -11
示例:
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 26 27 28 29 30 31 32 public class MathExample { public static void main (String[] args) { System.out.println("绝对值: " + Math.abs(-10 )); System.out.println("最大值: " + Math.max(10 , 20 )); System.out.println("最小值: " + Math.min(10 , 20 )); System.out.println("2 的 3 次方: " + Math.pow(2 , 3 )); System.out.println("平方根: " + Math.sqrt(16 )); System.out.println("立方根: " + Math.cbrt(27 )); System.out.println("向上取整: " + Math.ceil(4.3 )); System.out.println("向下取整: " + Math.floor(4.9 )); System.out.println("四舍五入: " + Math.round(4.5 )); System.out.println("sin(30°): " + Math.sin(Math.toRadians(30 ))); System.out.println("cos(60°): " + Math.cos(Math.toRadians(60 ))); System.out.println("tan(45°): " + Math.tan(Math.toRadians(45 ))); System.out.println("随机数 (0~1): " + Math.random()); System.out.println("随机整数 (1~100): " + (int ) (Math.random() * 100 + 1 )); System.out.println("自然对数 log(10): " + Math.log(10 )); System.out.println("以 10 为底的对数 log10(1000): " + Math.log10(1000 )); System.out.println("e 的 2 次方: " + Math.exp(2 )); } }
Java 数组、Arrays类与ArrayList
数组
声明数组变量
1 2 3 dataType[] arrayRefVar; dataType arrayRefVar[];
创建数组
Java语言使用new操作符来创建数组
1 2 3 4 dataType[] arrayRefVar = new dataType [arraySize]; dataType[] arrayRefVar = {value0, value1, ..., valuek};
Arrays类
方法
说明
数组输出
Arrays.toString(array)
以字符串形式返回一维数组内容
Arrays.deepToString(array)
以字符串形式返回多维数组内容
数组排序
Arrays.sort(array)
对数组进行升序排序
Arrays.sort(array, Comparator c)
使用指定比较器对对象数组排序
Arrays.parallelSort(array)
并行排序,适用于大数组,提升性能
数组搜索
Arrays.binarySearch(array, key)
在 排序后的数组 中查找 key,返回索引(找不到返回负值)
数组填充
Arrays.fill(array, value)
用 value 填充整个数组
Arrays.fill(array, from, to, value)
用 value 填充 [from, to) 范围的元素
数组复制
Arrays.copyOf(array, newLength)
复制数组,并调整长度
Arrays.copyOfRange(array, from, to)
复制数组的 [from, to) 子范围
数组比较
Arrays.equals(array1, array2)
判断两个 一维数组 是否相等
Arrays.deepEquals(array1, array2)
判断两个 多维数组 是否相等
数组转换
Arrays.asList(array)
将数组转换为 List(适用于对象数组)
流操作(Java 8+)
Arrays.stream(array)
将数组转换为 Stream 流,以便使用流操作
Arrays.parallelPrefix(array, operator)
计算并行前缀操作
示例:
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 import java.util.Arrays;import java.util.List;import java.util.stream.IntStream;public class ArraysExample { public static void main (String[] args) { int [] numbers = {5 , 3 , 8 , 1 , 2 }; System.out.println("原始数组: " + Arrays.toString(numbers)); Arrays.sort(numbers); System.out.println("排序后: " + Arrays.toString(numbers)); int index = Arrays.binarySearch(numbers, 3 ); System.out.println("数字 3 的索引: " + index); int [] filledArray = new int [5 ]; Arrays.fill(filledArray, 7 ); System.out.println("填充后: " + Arrays.toString(filledArray)); int [] partialFilled = {1 , 2 , 3 , 4 , 5 }; Arrays.fill(partialFilled, 1 , 4 , 9 ); System.out.println("部分填充后: " + Arrays.toString(partialFilled)); int [] copiedArray = Arrays.copyOf(numbers, 7 ); System.out.println("复制后: " + Arrays.toString(copiedArray)); int [] copiedRange = Arrays.copyOfRange(numbers, 1 , 4 ); System.out.println("复制范围: " + Arrays.toString(copiedRange)); int [] array1 = {1 , 2 , 3 }; int [] array2 = {1 , 2 , 3 }; int [] array3 = {1 , 2 , 4 }; System.out.println("数组是否相等 (array1 和 array2): " + Arrays.equals(array1, array2)); System.out.println("数组是否相等 (array1 和 array3): " + Arrays.equals(array1, array3)); String[] strArray = {"Alice" , "Bob" , "Charlie" }; List<String> strList = Arrays.asList(strArray); System.out.println("转换为 List: " + strList); int sum = Arrays.stream(numbers).sum(); System.out.println("数组元素总和: " + sum); int [] largeArray = {10 , 5 , 8 , 2 , 7 }; Arrays.parallelSort(largeArray); System.out.println("并行排序后: " + Arrays.toString(largeArray)); int [][] matrix = { {1 , 2 , 3 }, {4 , 5 , 6 } }; System.out.println("二维数组: " + Arrays.deepToString(matrix)); int [][] matrix2 = { {1 , 2 , 3 }, {4 , 5 , 6 } }; System.out.println("二维数组是否相等: " + Arrays.deepEquals(matrix, matrix2)); } }
ArrayList
ArrayList 是 Java 集合框架(Java Collections Framework)中的一个类,实现了 List 接口,位于 java.util 包下。它提供了动态数组的实现,能够根据需要自动调整大小。ArrayList 是非同步的(不是线程安全的)。
ArrayList 的主要特点:
基于动态数组实现,自动扩容
实现了 List 接口,拥有 List 的所有功能
允许存储任何类型的对象,包括 null
提供随机访问(通过索引)功能,访问元素的时间复杂度为 O(1)
插入和删除操作可能需要移动元素,时间复杂度为 O(n)
ArrayList 与数组比较
特性
ArrayList
数组
大小
动态,可以自动增长或缩小
固定,创建时必须指定大小且不可改变
类型安全
使用泛型可以确保类型安全
只能存储同一类型元素
存储类型
只能存储对象,不能存储基本数据类型
可以存储基本数据类型和对象
API
提供丰富的方法(add, remove, contains等)
仅提供基本操作和length属性
性能
需要额外的方法调用和类型检查,性能略低
访问元素更直接,性能较高
内存使用
由于需要存储对象,会有额外的开销
更加紧凑,特别是对基本类型
便利性
提供更多便利功能,如动态调整大小
需要手动处理扩容、缩容等操作
线程安全
不是线程安全的
不是线程安全的
迭代器
提供迭代器和增强for循环支持
只能使用索引或增强for循环
ArrayList 的创建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 ArrayList list1 = new ArrayList (); ArrayList<String> list2 = new ArrayList <String>(); ArrayList<String> list3 = new ArrayList <>(); ArrayList<Integer> list4 = new ArrayList <>(20 ); ArrayList<Integer> list5 = new ArrayList <>(Arrays.asList(1 , 2 , 3 , 4 , 5 ));
添加元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ArrayList<String> fruits = new ArrayList <>(); fruits.add("苹果" ); fruits.add("香蕉" ); fruits.add("橙子" ); fruits.add(1 , "葡萄" ); List<String> moreFruits = Arrays.asList("西瓜" , "芒果" ); fruits.addAll(moreFruits); fruits.addAll(2 , Arrays.asList("梨" , "樱桃" ));
访问元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ArrayList<String> fruits = new ArrayList <>(Arrays.asList("苹果" , "香蕉" , "橙子" ));String firstFruit = fruits.get(0 ); int size = fruits.size(); boolean hasApple = fruits.contains("苹果" ); boolean hasGrape = fruits.contains("葡萄" ); int index = fruits.indexOf("香蕉" ); int lastIndex = fruits.lastIndexOf("香蕉" ); boolean isEmpty = fruits.isEmpty();
修改元素
1 2 3 4 ArrayList<String> fruits = new ArrayList <>(Arrays.asList("苹果" , "香蕉" , "橙子" )); fruits.set(1 , "葡萄" );
删除元素
1 2 3 4 5 6 7 8 9 10 11 12 13 ArrayList<String> fruits = new ArrayList <>(Arrays.asList("苹果" , "香蕉" , "橙子" , "香蕉" )); fruits.remove("香蕉" ); fruits.remove(0 ); fruits.removeIf(fruit -> fruit.equals("香蕉" )); fruits.clear();
遍历元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ArrayList<String> fruits = new ArrayList <>(Arrays.asList("苹果" , "香蕉" , "橙子" ));for (int i = 0 ; i < fruits.size(); i++) { System.out.println(fruits.get(i)); }for (String fruit : fruits) { System.out.println(fruit); } Iterator<String> iterator = fruits.iterator();while (iterator.hasNext()) { System.out.println(iterator.next()); } fruits.forEach(fruit -> System.out.println(fruit)); fruits.forEach(System.out::println);
排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 ArrayList<String> fruits = new ArrayList <>(Arrays.asList("橙子" , "苹果" , "香蕉" )); Collections.sort(fruits); Collections.sort(fruits, Collections.reverseOrder()); fruits.sort(Comparator.naturalOrder()); fruits.sort(Comparator.reverseOrder()); fruits.sort(Comparator.comparingInt(String::length));
转换为数组
1 2 3 4 5 6 7 8 9 10 11 ArrayList<String> fruits = new ArrayList <>(Arrays.asList("苹果" , "香蕉" , "橙子" )); Object[] objectArray = fruits.toArray(); String[] stringArray1 = fruits.toArray(new String [0 ]); String[] stringArray2 = fruits.toArray(new String [fruits.size()]); String[] stringArray3 = fruits.toArray(String[]::new );
性能注意事项
随机访问 :ArrayList的随机访问性能很好,时间复杂度为O(1)
添加元素 :在末尾添加元素通常很快O(1),但可能会触发扩容操作
插入/删除元素 :在中间插入或删除元素需要移动后面的元素,时间复杂度为O(n)
初始容量 :如果预先知道大致元素数量,指定初始容量可以减少扩容操作
迭代 :迭代过程中修改列表(add/remove)会导致ConcurrentModificationException
适用场景
ArrayList适合以下场景:
需要频繁随机访问元素
主要在列表末尾添加/删除元素
对存储空间要求不高
不需要频繁在列表中间插入/删除元素
不需要线程安全
不适合的场景(考虑使用LinkedList):
频繁在列表中间或开头插入/删除元素
需要高效的栈、队列或双端队列操作
Java String类与StringBulider类
String类
注意: String类是不可改变的,所以你一旦创建了String对象,那它的值就无法改变了
String创建的字符串存储在公共池中,而new创建的字符串对象在堆上
1 2 3 4 5 String s1 = "Runoob" ; String s2 = "Runoob" ; String s3 = s1; String s4 = new String ("Runoob" ); String s5 = new String ("Runoob" );
其中 s1==s2 s2==s3 s3!=s4 s4!=s5
但是他们都equal(实际内容相同)
常用方法
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 String s1 = "Hello" ; String s2 = new String ("Hello" ); char [] chars = {'H' , 'e' , 'l' , 'l' , 'o' };String s3 = new String (chars); int length = s1.length();char ch = s1.charAt(1 ); int index = s1.indexOf('l' ); int lastIndex = s1.lastIndexOf('l' ); boolean contains = s1.contains("He" ); boolean isEqual = s1.equals(s2); boolean ignoreCaseEqual = s1.equalsIgnoreCase("hello" ); int compare = s1.compareTo("Hello" ); String sub1 = s1.substring(1 ); String sub2 = s1.substring(1 , 4 ); String replaced = s1.replace('l' , 'x' ); String replacedAll = s1.replaceAll("l+" , "X" ); String replacedFirst = s1.replaceFirst("l" , "X" ); String[] parts = "a,b,c" .split("," ); String[] parts2 = "a b c" .split("\\s+" ); String joined = String.join("-" , "Java" , "String" ); String concatStr = s1.concat(" World" ); String upper = s1.toUpperCase(); String lower = s1.toLowerCase(); String trimmed = " Hello " .trim(); String stripped = " Hello " .strip(); boolean isEmpty = s1.isEmpty(); boolean isBlank = s1.isBlank(); String formatted = String.format("Name: %s, Age: %d" , "Alice" , 25 );
StringBulider类
StringBulider的字符串可以修改,但是不是线程安全的
要求线程安全就用StringBuffer
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 StringBuilder sb1 = new StringBuilder (); StringBuilder sb2 = new StringBuilder ("Hello" ); StringBuilder sb3 = new StringBuilder (50 ); sb1.append("Hello" ); sb1.append(123 ); sb1.append(true ); sb1.append(3.14 ); sb1.insert(5 , " World" ); sb1.delete(5 , 11 ); sb1.deleteCharAt(5 ); sb1.replace(0 , 5 , "Hi" ); sb1.reverse(); int length = sb1.length(); int capacity = sb1.capacity(); sb1.setLength(10 ); char ch = sb1.charAt(2 ); sb1.setCharAt(2 , 'X' ); String result = sb1.toString();
Java 异常处理
1 2 3 4 5 6 7 8 9 try { }catch (异常类型1 异常的变量名1 ){ }catch (异常类型2 异常的变量名2 ){ }finally { }
或者
1 2 3 public void readFile () throws IOException { }
Java 方法
方法的定义
1 2 3 4 5 6 修饰符 返回值类型 方法名(参数类型 参数名){ ... 方法体 ... return 返回值; }
方法命名应使用小驼峰如addTest
修饰符 :修饰符,这是可选的,告诉编译器如何调用该方法。定义了 该方法的访问类型。
返回值类型 :方法可能会返回值。returnValueType 是方法返回值的数据类型。有些方法执行所需的操作,但没有返回值。在这种情况下,returnValueType 是关键字void。
方法名 :是方法的实际名称。方法名和参数表共同构成方法签名。
参数类型 :参数像是一个占位符。当方法被调用时,传递值给参数。这个值被称为实参或变量。参数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包含任何参数。
方法体 :方法体包含具体的语句,定义该方法的功能。
方法重载
重载的方法必须拥有不同的参数列表 。你不能仅仅依据修饰符或者返回类型的不同来重载方法。
构造方法
构造方法(Constructor)是用于创建类的对象的特殊方法。当使用 new 关键字创建对象时,构造方法会自动调用,用来初始化对象的属性。
构造方法特点:
方法名与类名相同 :构造方法的名字必须和类名一致。
没有返回类型 :构造方法没有返回类型,连 void 也不能写。
在创建对象时自动调用 :每次使用 new 创建对象时,都会自动调用构造方法。
可以重载 :可以为同一个类定义多个构造方法,但这些构造方法的参数列表必须不同(即构成重载)。
不管你是否自定义构造方法,所有的类都有构造方法,因为 Java 自动提供了一个默认构造方法,默认构造方法的访问修饰符和类的访问修饰符相同(类为 public,构造函数也为 public;类改为 protected,构造函数也改为 protected),一旦你定义了自己的构造方法,默认构造方法就会失效。
示例:
1 2 3 4 5 6 7 8 9 class MyClass { int x; MyClass(int i ) { x = i; } }
构造方法中的this关键字:
引用当前对象的属性或方法
调用另一个构造方法
示例:
1 2 3 4 5 6 7 8 public Person (String name) { this (name, 0 ); }public Person (String name, int age) { this .name = name; this .age = age; }
静态变量与静态方法
静态变量与静态方法是属于类而不是类的实例(对象),使用关键字static来声明。因为静态方法属于类本身,所以无需创建该类的实例就可以直接调用
静态方法特点:
不需要创建对象即可调用:通过类名直接调用
不能访问或修改实例变量:只能访问静态变量(类变量)
不能使用this或super关键字:因为静态方法与实例无关
不能被重写:静态方法可以被子类隐藏(覆盖),但不能被重写
不能直接调用非静态方法:在静态方法中只能直接调用其他静态方法
声明:
1 2 3 4 5 6 7 8 9 10 11 12 public class ClassName { private static dataType variableName = initialValue; public static final dataType CONSTANT_NAME = value; public static returnType methodName (parameters) { } }
调用:
1 2 3 4 5 ClassName.variableName; ClassName.methodName(arguments);
可变参数
可变参数允许程序员定义一个方法,该方法可以接受零个或多个相同类型的参数。
声明:
1 2 3 returnType methodName (dataType... parameterName) { }
可变参数必须是方法的最后一个参数
一个方法中只能有一个可变参数
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class VarargsWithArrayExample { public static int sum (int ... numbers) { int total = 0 ; for (int num : numbers) { total += num; } return total; } public static void main (String[] args) { int result1 = sum(1 , 2 , 3 , 4 , 5 ); System.out.println("Sum of individual elements: " + result1); int [] myArray = {1 , 2 , 3 , 4 , 5 }; int result = sum(myArray); System.out.println("Sum of array elements: " + result); } }
参数默认值
Java方法的参数没有默认值 ,但可以通过其他方法来实现类似的功能
方法重载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class DefaultValueExample { public void display (String name, int age, String city) { System.out.println("Name: " + name); System.out.println("Age: " + age); System.out.println("City: " + city); } public void display (String name, int age) { display(name, age, "Beijing" ); } public void display (String name) { display(name, 18 , "Beijing" ); } public static void main (String[] args) { DefaultValueExample example = new DefaultValueExample (); example.display("Zhang San" , 25 , "Shanghai" ); example.display("Li Si" , 30 ); example.display("Wang Wu" ); } }
可变参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class OptionalParamExample { public void process (String name, Object... options) { int age = (options.length > 0 && options[0 ] != null ) ? (int ) options[0 ] : 18 ; String city = (options.length > 1 && options[1 ] != null ) ? (String) options[1 ] : "Beijing" ; System.out.println("Name: " + name); System.out.println("Age: " + age); System.out.println("City: " + city); } public static void main (String[] args) { OptionalParamExample example = new OptionalParamExample (); example.process("Zhang San" , 25 , "Shanghai" ); example.process("Li Si" , 30 ); example.process("Wang Wu" ); } }
Java8中的Option类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class OptionalExample { public void greet (String name, Optional<Integer> age, Optional<String> city) { System.out.println("Name: " + name); System.out.println("Age: " + age.orElse(18 )); System.out.println("City: " + city.orElse("Beijing" )); } public static void main (String[] args) { OptionalExample example = new OptionalExample (); example.greet("Zhang San" , Optional.of(25 ), Optional.of("Shanghai" )); example.greet("Li Si" , Optional.of(30 ), Optional.empty()); example.greet("Wang Wu" , Optional.empty(), Optional.empty()); } }
Java 面向对象
Java 作为一种面向对象的编程语言,支持以下基本概念:
类(Class)
对象(Object)
封装(Encapsulation)
继承(Inheritance)
多态(Polymorphism)
抽象(Abstraction)
接口(Interface)
方法(Method)
方法重载(Method Overloading)
Java 类与对象
类 是定义对象的蓝图,包括属性和方法
对象 是类的实例,具有状态和行为
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class Dog { String name; int size; String colour; int age; void eat () { } void run () { } void sleep () { } void name () { } }
访问实例变量和方法:
1 2 3 4 5 6 7 8 Object referenceVariable = new Constructor (); referenceVariable.variableName; referenceVariable.methodName();
封装
将对象的状态(字段)私有化,通过公共方法访问
示例:
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 public class Puppy { private int age; private String name; public Puppy (String name) { this .name = name; System.out.println("小狗的名字是 : " + name); } public void setAge (int age) { this .age = age; } public int getAge () { return age; } public String getName () { return name; } public static void main (String[] args) { Puppy myPuppy = new Puppy ("Tommy" ); myPuppy.setAge(2 ); int age = myPuppy.getAge(); System.out.println("小狗的年龄为 : " + age); System.out.println("变量值 : " + myPuppy.getAge()); } }
继承
继承就是子类继承父类的特征和行为,使得子类对象实例具有父类的实例域或方法
1 2 3 4 5 class 父类 { } class 子类 extends 父类{ }
注意Java不支持多继承,即一个子类继承自多个父类
但支持多重继承,即多个子类继承一个父类
能够继承的东西:
private属性的和构造方法均不能被继承
如果子类没有同名的静态方法,可以直接通过子类调用父类的静态方法,在多态中由引用类型决定调用谁的静态方法
继承主要通过两种方式实现:
类继承(extends):子类继承父类
接口实现(implements):类实现接口
接口可以extends继承接口,并支持多继承
类继承extends
用于类与类之间的继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Animal { void eat () { System.out.println("This animal eats food." ); } }class Dog extends Animal { void bark () { System.out.println("The dog barks." ); } }public class Main { public static void main (String[] args) { Dog dog = new Dog (); dog.eat(); dog.bark(); } }
接口实现implements
用于类与接口之间的继承,从而使类实现一个接口
实现类必须实现接口中声明的所有方法,除非接口有默认方法,或该类为抽象类(将重写任务交给子类)
一个类可以继承自多个接口
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 26 27 28 29 30 31 32 interface Animal { void eat () ; void sleep () ; default void run () { System.out.println("Run" ); } }class Dog implements Animal { @Override public void eat () { System.out.println("The dog eats." ); } @Override public void sleep () { System.out.println("The dog sleeps." ); } void bark () { System.out.println("The dog barks." ); } }public class Main { public static void main (String[] args) { Dog dog = new Dog (); dog.eat(); dog.sleep(); dog.bark(); } }
super与this关键字
super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类
this关键字:指向自己的引用,引用当前对象,即它所在的方法或构造函数所属的对象实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Animal { void eat () { System.out.println("animal : eat" ); } } class Dog extends Animal { void eat () { System.out.println("dog : eat" ); } void eatTest () { this .eat(); super .eat(); } } public class Test { public static void main (String[] args) { Animal a = new Animal (); a.eat(); Dog d = new Dog (); d.eatTest(); } }
final关键字
final可以用来修饰变量(包括类属性、对象属性、局部变量和形参)、方法(包括类方法和对象方法)和类
使用final关键字声明类,就是把类定义定义为最终类,不能被继承,或者用于修饰方法,该方法不能被子类重写,修饰变量时,该变量只能被赋值一次
声明类:
声明方法:
1 2 3 修饰符 final 返回值类型 方法名(){ }
final定义的类,其中的属性、方法不是final的
继承中的构造器
子类是不继承父类的构造器(构造方法或者构造函数)的,它只是调用(隐式或显式)
如果父类的构造器带有参数,则必须在子类的构造器中显式地通过super关键字调用父类的构造器并配以适当的参数列表
如果父类构造器没有参数,则在子类的构造器中不需要使用super关键字调用父类构造器,系统会自动调用父类的无参构造器
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 26 27 28 29 30 31 class SuperClass { private int n; public SuperClass () { System.out.println("SuperClass()" ); } public SuperClass (int n) { System.out.println("SuperClass(int n)" ); this .n = n; } } class SubClass extends SuperClass { private int n; public SubClass () { System.out.println("SubClass()" ); } public SubClass (int n) { super (300 ); System.out.println("SubClass(int n): " + n); this .n = n; } }
多态
多态是指同一个操作或方法可以在不同的对象上有不同的行为表现
在Java中,多态允许一个对象在不同情况下表现出不同的形态,主要通过方法的重写(Override)和重载(Overload)来实现
多态主要有两种形式:
编译时多态(静态多态):通过方法重载实现
运行时多态(动态多态):通过方法重写实现
重载(Overload) - 静态多态
重载是在一个类里面,方法名字相同,而参数不同 。返回类型可以相同也可以不同
每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表
方法能够在同一个类中或者在一个子类中被重载
最常用的如构造器重载
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class Overloading { public int test () { System.out.println("test1" ); return 1 ; } public void test (int a) { System.out.println("test2" ); } public String test (int a,String s) { System.out.println("test3" ); return "returntest3" ; } public String test (String s,int a) { System.out.println("test4" ); return "returntest4" ; } }
重写(Override)
重写(Override)是指子类定义了一个与其父类中具有相同名称、参数列表和返回类型的方法,并且子类方法的实现覆盖了父类方法的实现
运行时多态是使用继承和接口实现的,也是最常见的多态形式
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Animal { public void move () { System.out.println("动物可以移动" ); } } class Dog extends Animal { @Override public void move () { System.out.println("狗可以跑和走" ); } } public class TestDog { public static void main (String args[]) { Animal a = new Animal (); Animal b = new Dog (); a.move(); b.move(); } }
一般会在重写方法上面加上@Override注解
注意:
参数列表 与被重写方法的参数列表必须完全相同
返回类型与被重写方法的返回类型 可以不相同,但是必须是父类返回值的派生类
访问权限 不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected
声明为final的方法不能被重写
声明为static的方法不能被重写,但是能够被再次声明
子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法
子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法
重写的方法能够抛出任何非强制异常 ,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以
构造方法不能被重写
动态多态
动态多态 存在的三个必要条件:
继承
重写
父类引用指向子类对象:Parent p = new Child();即向上转型 (Upcasting)
Parent p = new Child();可以分为三个部分:
Parent p 声明一个父类类型的引用变量
new Child() 创建一个子类的对象实例(堆内存中分配空间给Child对象)
= 将子类对象的引用赋给父类类型的变量
引用变量p的静态类型(编译时类型)是Parent
引用变量p指向的对象的动态类型(运行时类型)是Child
可以做什么:
调用被子类重写的方法
当通过父类引用调用被子类重写的方法时,会执行子类版本的方法实现(动态方法调度)
调用父类中定义的所有方法,即使这些方法没有被子类重写
访问父类中定义的所有变量
不能做什么:
不能直接调用子类特有的方法
不能直接调用子类特有的属性
示例:
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 26 27 28 29 30 31 class Vehicle { public void start () { System.out.println("车辆启动" ); } }class Car extends Vehicle { @Override public void start () { System.out.println("汽车启动" ); } public void openTrunk () { System.out.println("打开后备箱" ); } }public class Test { public static void main (String[] args) { Vehicle vehicle = new Car (); vehicle.start(); if (vehicle instanceof Car) { Car car = (Car) vehicle; car.openTrunk(); } } }
向下转型(Downcasting)
要访问子类特有的成员,需要进行向下转型
向下转型需要谨慎使用,应该先用instanceof操作符检查对象的实际类型,以避免ClassCastException异常。
1 2 3 4 5 6 7 8 9 10 11 12 Parent p = new Child ();if (p instanceof Child) { Child c = (Child) p; c.childOnly(); } if (p instanceof Child c) { c.childOnly(); }
应用场景示例:
集合类中的使用
1 2 3 4 5 6 7 8 9 List<Animal> animals = new ArrayList <>(); animals.add(new Dog ()); animals.add(new Cat ()); animals.add(new Bird ());for (Animal animal : animals) { animal.makeSound(); }
方法参数中的使用
1 2 3 4 5 6 7 8 public void feedAnimal (Animal animal) { animal.eat(); } feedAnimal(new Dog ()); feedAnimal(new Cat ());
工厂模式中的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 public Shape createShape (String type) { if (type.equals("circle" )) { return new Circle (); } else if (type.equals("rectangle" )) { return new Rectangle (); } return null ; }Shape shape = createShape("circle" ); shape.draw();
抽象类
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类
抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样
由于抽象类不能实例化对象 ,所以抽象类必须被继承 ,才能被使用。也是因为这个原因,通常在设计阶段决定要不要设计抽象类
示例:
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 abstract class Animal { public abstract void makeSound () ; public void sleep () { System.out.println("Zzz..." ); } }class Dog extends Animal { public void makeSound () { System.out.println("汪汪!" ); } }class Cat extends Animal { public void makeSound () { System.out.println("喵喵!" ); } }public class Main { public static void main (String[] args) { Animal dog = new Dog (); Animal cat = new Cat (); dog.makeSound(); dog.sleep(); cat.makeSound(); cat.sleep(); } }
注意:
抽象类不能被实例化,只有抽象类的非抽象子类可以创建对象
抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类
构造方法,静态的类方法(用 static 修饰的方法)不能声明为抽象方法
抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类
接口
在 Java 中,接口是一种特殊的抽象类型,它定义了一组方法,但不提供实现。接口用于规定一个类必须实现哪些行为(方法),起到一种“规范”或“契约”作用
1 2 3 4 interface Animal { void makeSound () ; }
通过implements关键字来实现接口,并且必须实现接口中定义的所有方法
1 2 3 4 5 class Dog implements Animal { public void makeSound () { System.out.println("汪汪!" ); } }
Java 8 以后的接口增强:
默认方法(default):接口可以提供默认实现
静态方法(static):可以直接通过接口调用
私有方法(Java 9 起):接口中也可以写私有方法来给默认方法做辅助
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 interface Animal { default void eat () { logAction("正在吃东西" ); } static void showInfo () { logStatic("这是一个动物接口" ); } void makeSound () ; private void logAction (String action) { System.out.println("动物:" + action); } }
抽象类与接口的区别:
语法层面上的区别
抽象类可以提供成员方法的实现细节,而接口中只能存在public abstract 方法
抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的
接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法
一个类只能继承一个抽象类,而一个类却可以实现多个接口
设计层面上的区别
抽象类是对一种事物的抽象,即对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。举个简单的例子,飞机和鸟是不同类的事物,但是它们都有一个共性,就是都会飞。那么在设计的时候,可以将飞机设计为一个类 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() 两个动作,此时我们可以定义通过抽象类和接口来定义这个抽象概念:
1 2 3 4 abstract class Door { public abstract void open () ; public abstract void close () ; }
或者:
1 2 3 4 interface 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 接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 interface Alram { void alarm () ; } abstract class Door { void open () ; void close () ; } class AlarmDoor extends Door implements Alarm { void oepn () { } void close () { } void alarm () { } }
Java包
源文件声明规则:
当在一个源文件中定义多个类,并且还有 import 语句和 package 语句时,要特别注意这些规则。
一个源文件中只能有一个public类
一个源文件可以有多个非public类
源文件的名称应该和public类的类名保持一致
如果一个类定义在某个包中,那么package语句应该在源文件的首行。
如果源文件包含import语句,那么应该放在package语句和类定义之间。如果没有package语句,那么import语句应该在源文件中最前面
import语句和package语句对源文件中定义的所有类都有效。在同一源文件中,不能给不同的类不同的包声明
package关键字:
1 package pkg1[.pkg2[.pkg3…]];
示例:
1 2 3 4 5 package net.java.util;public class Something { ... }
那么它的路径应该是net/java/util/Something.java这样保存的。 包的作用是把不同的 java 程序分类保存,更方便的被其他 java 程序调用
import关键字:
1 import package1[.package2…].(classname|*);
import语句应位于package语句之后
示例:
1 2 3 4 5 package com.example;import java.util.ArrayList; import java.util.*;
杂鱼 ❤~
单词术语速记
单词术语
释义
单词术语
释义
explicitly declared
显示声明
Assignment operator
赋值运算符
Overload
重载
Override
重写
invoke
调用
instantiate
实例化
Upcasting
向上转型
Downcasting
向下转型
static polymorphism
静态多态
dynamic polymorphism
动态多态
interface
接口
implement
实现
complier
编译器
termination
终止
值传递
Java 中所有参数传递都是值传递(pass-by-value),这是 Java 的一项语言特性
示例:
1 2 3 4 5 6 7 void change (int x) { x = 5 ; }int a = 10 ; change(a); System.out.println(a);
1 2 3 4 5 6 7 8 void changeName (Person p) { p.name = "Alice" ; }Person person = new Person (); person.name = "Bob" ; changeName(person); System.out.println(person.name);
1 2 3 4 5 6 7 8 9 void changeName (Person p) { p = new Person (); p.name = "Charlie" ; }Person person = new Person (); person.name = "Bob" ; changeName(person); System.out.println(person.name);
==和equals()
== 的作用
对于基本数据类型(如int、char、boolean等),==比较的是它们的实际数值是否相等。
对于引用类型(即对象),==比较的是两个对象的内存地址(引用)是否相同,也就是说是否指向同一个对象实例。
示例:
1 2 3 4 5 6 String a = "hello" ;String b = "hello" ;String c = new String ("hello" ); System.out.println(a == b); System.out.println(a == c);
equals() 的作用
equals()是Object类中的一个方法,用于比较两个对象的内容是否相等。
默认情况下(即没有重写equals()方法时),equals()的实现就是调用==,比较的是对象的引用地址。
许多类(如String、Integer等)重写了equals()方法,使其比较对象的实际内容 ,而不是引用地址
示例:
1 2 3 4 String a = new String ("hello" );String b = new String ("hello" ); System.out.println(a.equals(b));
如果自定义类没有重写equals(),则比较的是引用地址:
1 2 3 4 5 6 7 8 9 class Cat { String name; Cat(String name) { this .name = name; } }Cat c1 = new Cat ("Tom" );Cat c2 = new Cat ("Tom" ); System.out.println(c1.equals(c2));