单行注释
// 单行注释
多行注释
/*
多行注释
*/
文档注释
/**
*@Description Hello World
*@Author
*/
关键字
abstract; boolean; break; byte; case; catch; char; class; continue; default; do; double; else; extends; false; final; finally; float; for; if; implements; import; instanceof; int; interface; long; native; new; null; package; private; protected; public; return; short; static; super; switch; synchronized; this; throw; throws; transient; true; try; void; volatile; while
标识符
基本数据类型
数值类型
整数类型
byte
占 1 个字节,min:-128(-27)max:127(27-1)short
占 2 个字节,min:-32768(-215)max:32767(215-1)int
占 4 个字节,默认值 0,min:-2,147,483,648(-231)max:2,147,483,647(231-1)long
占 8 个字节,min:-9,233,372,036,854,775,808(-263)max:9,223,372,036,854,775,807(263-1)浮点类型
float
占 4 个字节double
占 8 个字节字符类型
char
占 2 个字节,默认值null
boollean
类型:占 1 位,其值只有true
和false
两个,默认值false
引用数据类型
整数
浮点数
字符
转义字符
低 -----------------------------------------------------------------------> 高
byte
-> short
, char
-> int
-> long
-> float
-> double
强制类型转换
自动类型转换
注意点
不能对布尔值进行转换
不能把对象类型转换为不相干的类型
在把高容量转换到低容量的时候,强制转换
转换的时候可能存在内存溢出,或者精度问题
//JDK7新特性,数字之间可以用下划线分割
int money = 10_0000_0000;
int years = 20;
int total = money*years; // -1474836480,计算的时候溢出了
long total2 = money*years; // 还是-1474836480,因为右边还是int类型,转换之前已经存在问题了
long total3 = money*((long)years); // 2000000000,先把一个数转换为long,则全部转换为long
变量
类变量
实例变量(从属于对象)
局部变量(从属于方法,必须声明和初始化值)
public class Variable {
static int allClick = 0; //类变量
String str = "Hello world"; //实例变量
public void method() {
int i = 0; //局部变量
}
}
常量(Constant)
初始化(initialize)后不能再改变值
常量名一般使用大写字符
final double PI = 3.14;
命名规范
算术运算符 +, -, *, /, %, ++, –
int
以下精度运算(short
,byte
),结果自动转化为int
幂运算 2^3
double pow = Math.pow(2,3);
赋值运算符 =
关系运算符 >, <, >=, <=, ==, !=, instanceof
逻辑运算符 &&, ||, !
注意短路运算
boollean a = true;
boollean b = false;
System.out.println(a && b); //false,先执行判断a,后执行判断b
System.out.println(b && a); //false,先执行判断b,直接短路,不执行判断a
位运算符 &, |, ^, ~, >>, <<, >>>
&, |, ~, ^:位运算的与,或,非,异或
A = 0011 1100
B = 0000 1101
A & B //与,0000 1100
A | B //或,0011 1101
A ^ B //异或,0011 0001
~ B //非,1111 0010
2 × 8 最快运算方法?
<<
左移,相当于 × 2
>>
右移,相当于 ÷ 2
System.out.println(2 << 3);
条件运算符 ? :
扩展赋值运算符 +=, -=, *=, /=
为了更好地组织类,Java 提供了包机制,用于区别类名的命名空间。
包语句的语法格式为:
package pkg1[. pkg2[. pkg3...]];
一般利用公司域名倒置作为包名;
为了能够使用某一个包的成员,需要在Java程序中明确导入该包,使用import
语句可完成此功能:
import package1[. package2...].(classname|*);
javadoc
生成文档javadoc
命令是用来生成自己 API 文档的@author
作者名@version
版本号@since
指明需要最早使用的jdk版本@param
参数名@return
返回值情况@throws
异常抛出情况Scanner
基本语法
Scanner scanner = new Scanner(System.in);
通过Scanner
类的next()
与nextLine()
方法获取输入的字符串,在读取前一般需要使用hasNext()
与hasNextLine()
判断是否还有输入的数据。
import java.util.Scanner;
public class Hello {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("使用next方式接收:");
if(scanner.hasNext()){
String str = scanner.next();
System.out.println("输入的内容为:"+str);
}
// 凡是属于IO流的类如果不关闭会一直占用资源,应该使用完就关掉。
scanner.close();
}
}
next()
:
next()
方法会自动将其去掉next()
不能得到带有空格的字符串nextLine()
:
nextLine()
方法返回的是输入回车之前的所有字符Scanner
进阶使用输入整数
import java.util.Scanner;
public class Hello {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int i;
if (scanner.hasNextInt()) {
i = scanner.nextInt();
System.out.println(i);
} else {
System.out.println("输入的不是整数。");
}
scanner.close();
}
}
输入多个数字,并求其总和与平均数,每输入一个数字用回车确认,通过输入非数字来结束输入并输出执行结果
import java.util.Scanner;
public class Hello {
public static void main(String[] args) {
double sum = 0.0D;
int m = 0;
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextDouble()) {
sum += scanner.nextDouble();
m++;
}
System.out.println("总和为:" + sum + "\n平均数为:" + sum / m);
scanner.close();
}
}
if
选择结构if
单选择结构
if (布尔表达式) {
// 如果布尔表达式的值为 true 执行代码
}
if
双选择结构
if (布尔表达式) {
// 如果布尔表达式的值为 true 执行代码
} esle {
// 如果布尔表达式的值为 false 执行代码
}
if
多选择结构
// 一旦其中一个 else if 语句检测为 true,其他的 else if 以及 else 语句都将跳过执行
if (布尔表达式 1) {
// 如果布尔表达式 1 的值为 true 执行代码
} esle if (布尔表达式 2) {
// 如果布尔表达式 2 的值为 true 执行代码
} esle if (布尔表达式 3) {
// 如果布尔表达式 3 的值为 true 执行代码
} esle {
// 如果以上布尔表达式的值均为 false 执行代码
}
嵌套的if
结构
if (布尔表达式 1) {
// 如果布尔表达式 1 的值为 true 执行代码
if(布尔表达式 2) {
// 如果布尔表达式 2 的值为 true 执行代码
}
}
switch
多选择结构switch
语句中的变量类型可以是byte
、short
、int
或者char
从 Java SE 7 开始,switch
支持字符串String
类型了,同时case
标签必须为字符串常量或字面量
switch (expression) {
case value:
// 语句
break; // 可选
case value:
// 语句
break; // 可选
default: // 可选
// 语句
}
case
穿透现象:从满足条件的case value
开始,执行后面所有的case
,直到遇到 break
停止
while
循环while
是最基本的循环,其结构为:
while (布尔表达式) {
// 循环内容
}
只要布尔表达式为true
循环就会一直执行下去
大多数情况会让循环停止下来,需要一个让表达式失效的方式来结束循环
少部分情况需要循环一直执行,比如服务器的请求响应监听等
do
…while
循环对于while
语句而言,如果不满足条件,则不能进入循环
do
…while
循环和while
循环相似,不同的是,do
…while
循环至少会执行一次
do {
// 代码语句
} while (布尔表达式);
for
循环for
循环执行的次数在执行前就确定,语法格式如下:
for (初始化;布尔表达式;更新) {
// 代码语句
}
IDEA 快捷键:100.for
后回车,自动生成:
for (int i = 0; i < 100; i++) {
}
练习1:计算0到100之间的奇数和偶数的和
public static void main(String[] args) {
int oddsum = 0;
int evensum = 0;
for (int i = 0; i < 100; i++) {
if (i % 2 != 0) {
oddsum += i;
} else {
evensum += i;
}
}
System.out.println("奇数的和:" + oddsum);
System.out.println("偶数的和:" + evensum);
}
练习2:用while
或for
循环输出 1-1000 之间能被 5 整除的数,每行输出 3 个
public static void main(String[] args) {
for (int i = 1; i < 1000; i++) {
if (i % 5 == 0) {
System.out.print(i + "\t");
}
if (i % 15 == 0) {
System.out.println();
}
}
}
练习3:打印九九乘法表
public static void main(String[] args) {
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= i; j++) {
System.out.print(j+"×"+i+"="+j*i+"\t");
}
System.out.println();
}
}
for
循环Java 5 引入了一种主要用于数组或集合的增强型for
循环
Java 增强for
循环语法格式如下:
for (声明语句 : 表达式)
{
// 代码句子
}
声明语句:声明新的局部变量,该变量的类型必须和数组元素的类型匹配。其作用域限定在循环语句块,其值与此时数组元素的值相等。
表达式:表达式是要访问的数组名,或者是返回值为数组的方法。
public static void main(String[] args) {
int[] numbers = {10, 20, 30, 40, 50};
for (int x : numbers) {
System.out.println(x);
}
}
break
、continue
、goto
在任何循环语句的主体部分,均可用break
控制循环的流程。break
用于强行退出循环,不执行循环中剩余的语句。
public static void main(String[] args) {
int i = 0;
while (i < 100) {
i++;
System.out.println(i);
if (i == 30) {
break; // 打破 while 循环,至此只输出到 30
}
}
System.out.println("123"); // 证明只是打破 while 循环,函数仍在执行。
}
continue
用于终止某次循环过程,即跳过循环体中尚未执行的语句,接着进行下一次是否执行循环的判定。
public static void main(String[] args) {
int i = 0;
while (i < 100) {
i++;
if (i % 10 == 0) {
System.out.println();
continue; // 后面输出 i 不执行,直接进入下一次 while 循环
}
System.out.print(i);
}
}
关于goto
关键字
goto
关键字很早就在程序设计语言中出现。尽管goto
仍是 Java 的一个保留字,但并未在语言中得到正式使用 —— Java 没有goto
。然而,在break
和continue
这两个关键字的身上,我们仍能看出一些goto
的影子 —— 带标签的break
和continue
。例如label:
// 输出 101 到 150 之间的质数
public static void main(String[] args) {
outer: for (int i = 101; i < 150; i++) {
for (int j = 2; j < i/2; j++) {
if (i % j == 0) {
continue outer; // 直接跳转到 outer:
}
}
System.out.print(i + " ");
}
}
对 Java 来说唯一用到标签的地方是在循环语句之前。而在循环之前设置标签的唯一理由是:我们希望在其中嵌套另一个循环,由于break
和continue
关键字通常只中断当前循环,但若随同标签使用,它们就会终端到存在标签的地方。
打印三角形
import java.util.Scanner;
public class Hello {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
for (int i = 0; i < n; i++) {
for (int j = 0; j < n-i-1; j++) {
System.out.print(" ");
}
for (int j = 0; j < 2*i+1; j++) {
System.out.print("*");
}
System.out.println();
}
}
}
打印菱形
import java.util.Scanner;
public class Hello {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
for (int i = 0; i < 2*n-1; i++) {
if (i < n) {
for (int j = 0; j < n-i-1; j++) {
System.out.print(" ");
}
for (int j = 0; j < 2*i+1; j++) {
System.out.print("*");
}
} else {
for (int j = 0; j < i-n+1; j++) {
System.out.print(" ");
}
for (int j = 0; j < 4*n-2*i-3; j++) {
System.out.print("*");
}
}
System.out.println();
}
}
}
方法的定义
修饰符:可选,告诉编译器如何调用该方法。定义了该方法的访问类型。
返回值类型:returnValueType
是方法返回值的数据类型。有些方法没有返回值,在此情况下,returnValueType
是关键字void
。
方法名:方法的实际名称。方法名和参数表共同构成方法签名。
参数类型:当方法被调用时,传递值给参数。这个值被称为实参或变量。参数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包含任何参数。
形式参数
实参
方法体:包含具体的语句,定义该方法的功能。
修饰符 返回值类型 方法名(参数类型 参数名) {
……
方法体
……
return 返回值;
}
方法调用
调用方法:对象名.方法名(实参列表)
Java 支持两种调用方法的方式,根据方法是否返回值来选择
当方法返回一个值的时候,方法调用通常被当作一个值
int larger = max(30, 40);
如果方法返回值是void
,方法调用一定是一条语句
System.out.println("Hello, world!");
值传递(Java) 和 引用传递
运行一个程序时候再传递给它消息,需要靠传递命令行参数给main()
方法实现
package method;
public class Demo03 {
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
System.out.println("args[" + i + "]: " + args[i]);
}
}
}
D:\Coding\Java\Learning>javac Demo03
错误: 仅当显式请求注释处理时才接受类名称 'Demo03'
1 个错误
D:\Coding\Java\Learning>cd D:\Coding\Java\Learning\src\method
D:\Coding\Java\Learning\src\method>javac Demo03.java
D:\Coding\Java\Learning\src\method>java Demo03
错误: 找不到或无法加载主类 Demo03
D:\Coding\Java\Learning\src\method>cd..\
D:\Coding\Java\Learning\src>java method.Demo03
D:\Coding\Java\Learning\src>java method.Demo03 this is Enchant_sword
args[0]: this
args[1]: is
args[2]: Enchant_sword
D:\Coding\Java\Learning\src>
JDK 1.5 开始,Java 支持传递同类型的可变参数给一个方法
在方法声明中,在指定参数类型后加一个省略号(…)
一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。任何普通的参数必须在它之前声明。
package method;
public class Demo04 {
public static void main(String[] args) {
// 调用可变参数的方法
printMax(34,3,3,2,56.5);
printMax(new double[] {1,2,3});
}
public static void printMax(double... numbers) {
if (numbers.length == 0) {
System.out.println("No argument passed");
return;
}
double result = numbers[0];
// 排序
for (int i = 1; i < numbers.length; i++) {
if (numbers[i] > result) {
result = numbers[i];
}
}
System.out.println("The max value is " + result);
}
}
递归头:什么时候不调用自身方法。如果没有头,将陷入死循环。
递归体:什么时候需要调用自身方法。
package method;
public class Demo06 {
public static void main(String[] args) {
System.out.println(f(5));
}
public static int f(int n) {
if (n == 1) { //递归头
return 1;
} else { //递归体,阶乘
return n * f(n-1);
}
}
}
声明数组变量
dataTypep[] arrayRefVar; // 首选方法
或
dataTypep arrayRefVar[]; // 效果相同,但非首选方法
Java 使用new
操作符来创建数组
dataTypep[] arrayRefVar = new dataType[arraySize];
数组通过索引访问,数组索引从 0 开始
获取数组长度:
arrays.length
package array;
public class Demo01 {
public static void main(String[] args) {
int[] nums; // 1.声明1个数组
nums = new int[10]; // 2.创建1个数组
nums[0] = 1; // 3.给数组中元素赋值
nums[1] = 2;
nums[2] = 3;
nums[3] = 4;
nums[4] = 5;
nums[5] = 6;
nums[6] = 7;
nums[7] = 8;
nums[8] = 9;
nums[9] = 10;
int sum = 0;
for (int i = 0; i < nums.length; i++) {
// 增强型 for 循环: for (int num : nums)
sum += nums[i];
}
System.out.println("总和为:" + sum);
}
}
Java 内存分析:
堆
new
的对象和数组栈
方法区
class
和static
变量三种初始化
静态初始化
int[] a = {1,2,3,4,5,6,7,8};
Man[] men = {new Man(1,1), new Man(2,2)};
动态初始化
int[] a = new int[2];
a[0] = 1;
a[1] = 2;
数组的默认初始化
数组的四个基本特点
数组边界
ArrayIndexOutOfBoundsException
:数组下标越界异常!普通的for
循环
for - each
循环 (增强型for
循环)
数组作方法入参
数组作返回值
package array;
public class Demo03 {
public static void main(String[] args) {
int[] arrays = {1,2,3,4,5};
printArray(arrays);
printArray(reverseArray(arrays));
}
// 打印数组元素(数组作方法入参)
public static void printArray(int[] arrays) {
// 普通的 for 循环
for (int i = 0; i < arrays.length; i++) {
// 增强的 for 循环:
for (int i : arrays) {
System.out.print(arrays[i] + " ");
}
}
}
// 反转数组(数组作返回值)
public static int[] reverseArray(int[] arrays) {
int[] result = new int[arrays.length];
for (int i = 0; i < arrays.length; i++) {
result[arrays.length - i - 1] = arrays[i];
}
return result;
}
}
多维数组可以看成是数组的数组,比如二维数组就是一个特殊的一维数组,其每一个元素都是一个一维数组
二维数组
// 可视作两行五列的数组
int a[][] = new int[2][5]
package array;
public class Demo04 {
public static void main(String[] args) {
// [4][2]
int[][] array = {{1,2},{2,3},{3,4},{4,5}};
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[i].length; j++) {
System.out.print(array[i][j] + "\t");
}
System.out.println();
}
}
}
Arrays
类Arrays
类中的方法都是static
修饰的静态方法,在使用的时候可以直接使用类名进行调用,而不用使用对象来调用(注意:是“不用”而非“不能”)
具有以下常用功能:
给数组赋值:通过fill
方法
对数组排序:通过sort
方法,按升序
比较数组:通过equals
方法比较数组中元素值是否相等
查找数组元素:通过binarySearch
方法能对排序好的数组进行二分查找法操作
package array;
import java.util.Arrays;
public class Demo05 {
public static void main(String[] args) {
int[] a = {4,7,23,534,76,334,2,56,67,33,45};
// 数组升序排序
Arrays.sort(a);
// 转成字符串输出
System.out.println(Arrays.toString(a));
// 将数组下标[2]到下标[8-1]的元素用 0 填充
Arrays.fill(a,2,8,0);
// 转成字符串输出
System.out.println(Arrays.toString(a));
}
}
嵌套循环,时间复杂度为 O(n^2)
package array;
import java.util.Arrays;
public class Demo06 {
public static void main(String[] args) {
int[] array = {3,5,756,23,5,4334,65,445,6,4,3,56};
int[] sort = sort(array);
System.out.println(Arrays.toString(sort));
}
public static int[] sort(int[] a) {
// 临时变量
int temp;
// 外层循环,判断循环执行次数
for (int i = 0; i < a.length - 1; i++) {
// 内层循环,比较两个数大小,并互换位置
for (int j = 0; j < a.length - 1 - i ; j++) {
// 改为 < 则降序排序
if (a[j] > a[j+1]) {
/*
核心代码,若不引入临时变量 temp,也可简化为
a[j] += a[j+1];
a[j+1] = a[j] - a[j+1];
a[j] -= a[j+1];
*/
temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
}
}
}
return a;
}
}
优化算法
for (int i = 0; i < a.length - 1; i++) {
// 放置标识位,减少无意义的比较
boollean flag = false;
for (int j = 0; j < a.length - 1 - i ; j++) {
if (a[j] > a[j+1]) {
temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
// 只要发生过 1 次 if 交换,则 flag 赋值 true,后面继续比较
flag = true;
}
}
if (!flag) {
break;
}
}
当一个数组中大部分元素为 0,或者为同一值时,可以使用稀疏数组来保存
稀疏数组的处理方式是:
记录数组一共有几行几列,有多少个不同值
把具有不同值的元素和行列及值记录在一个小规模的数组中,从而缩小程序的规模
原始数组:
[ 0 0 0 22 0 0 15 0 11 0 0 0 17 0 0 0 0 − 6 0 0 0 0 0 0 0 0 39 0 91 0 0 0 0 0 0 0 0 28 0 0 0 0 ] \left [ \begin{array}{c} 0 & 0 & 0 & 22 & 0 & 0 & 15 \\ 0 & 11 & 0 & 0 & 0 & 17 & 0 \\ 0 & 0 & 0 & -6 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 39 & 0 \\ 91 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 28 & 0 & 0 & 0 & 0 \\ \end{array} \right ] ⎣ ⎡000091001100000000028220−6000000000017039001500000⎦ ⎤
稀疏数组:
[ 行 列 值 ( r o w ) ( c o l ) ( v a l u e ) [ 0 ] 6 7 8 [ 1 ] 0 3 22 [ 2 ] 0 6 15 [ 3 ] 1 1 11 [ 4 ] 1 5 17 [ 5 ] 2 3 − 6 [ 6 ] 3 5 39 [ 7 ] 4 0 91 [ 8 ] 5 2 28 ] \left [ \begin{array}{c|cc} & 行 & 列 & 值 \\ & (row) & (col) & (value) \\ \hline [0] & 6 & 7 & 8 \\ [1] & 0 & 3 & 22 \\ [2] & 0 & 6 & 15 \\ [3] & 1 & 1 & 11 \\ [4] & 1 & 5 & 17 \\ [5] & 2 & 3 & -6 \\ [6] & 3 & 5 & 39 \\ [7] & 4 & 0 & 91 \\ [8] & 5 & 2 & 28 \\ \end{array} \right ] ⎣ ⎡[0][1][2][3][4][5][6][7][8]行(row)600112345列(col)736153502值(value)822151117−6399128⎦ ⎤
原始数组和稀疏数组互相转换
package array;
public class Demo07 {
public static void main(String[] args) {
// 创建一个 11 * 11 的二维数组
// 0:没有棋子 1:黑棋 2:白棋
int[][] array1 = new int[11][11];
array1[1][2] = 1;
array1[2][3] = 2;
// 输出原始的数组(①使用 2 层增强型 for 循环)
System.out.println("输出原始的数组");
for (int[] ints : array1) {
for (int anInt : ints) {
System.out.print(anInt + "\t");
}
System.out.println();
}
// 转换为稀疏数组保存
// 1.获取有效值的个数
int sum = 0;
for (int i = 0; i < 11; i++) {
for (int j = 0; j < 11; j++) {
if (array1[i][j] != 0) {
sum ++;
}
}
}
System.out.println("有效值的个数为:" + sum);
// 2.创建一个稀疏数组的数组
// 因第 0 行存放数组信息,稀疏数组行数为 sum + 1
int[][] array2 = new int[sum + 1][3];
// 列数固定为 3,分别为行坐标、列坐标、值
array2[0][0] = array1.length;
array2[0][1] = array1.length;
array2[0][2] = sum;
// 3.遍历二维数组,将非零的值存放于稀疏数组中
int count = 0;
for (int i = 0; i < array1.length; i++) {
for (int j = 0; j < array1[i].length; j++) {
if (array1[i][j] != 0) {
count ++;
array2[count][0] = i;
array2[count][1] = j;
array2[count][2] = array1[i][j];
}
}
}
// 4.输出转换后的稀疏数组(②使用 1 层常规 for 循环)
System.out.println("输出转换后的稀疏数组");
for (int[] item : array2) {
System.out.println(item[0] + "\t"
+ item[1] + "\t"
+ item[2] + "\t");
}
// 还原为原始数组
System.out.println("还原为原始数组");
int rowArray3 = array2[0][0];
int colArray3 = array2[0][1];
int countArray3 = array2[0][2];
int[][] array3 = new int[rowArray3][colArray3];
for (int i = 0; i < countArray3; i++) {
int row = array2[i + 1][0];
int col = array2[i + 1][1];
int value = array2[i + 1][2];
array3[row][col] = value;
}
// 输出还原后的原始数组(③使用 2 层常规 for 循环)
for (int i = 0; i < array3.length; i++) {
for (int j = 0; j < array3[i].length; j++) {
System.out.print(array3[i][j] + "\t");
}
System.out.println();
}
}
}
面向过程 & 面向对象
面向过程思想
步骤清晰简单,第一步、第二步……
适合处理较为简单的问题
面向对象思想
描述复杂的事物,为从宏观上把握、从整体上分析,需要使用面向对象的思路。但具体到微观操作,仍需要面向过程的思路去处理。
何为面向对象
面向对象编程(Object - Oriented Programming, OOP)
面向对象编程的本质是:以类的方式组织代码,以对象的方式组织(封装)数据。
抽象
从认识论角度考虑,是先有对象后有类。对象是具体的事物;类是对对象的抽象。
从代码运行角度考虑,是先有类后有对象。类是对象的模板。
break
和return
的区别:break
结束循环;return
结束方法。静态方法
package oop;
public class Demo02 {
public static void main(String[] args) {
// 调用静态方法
// 类名.方法名
Student.say1();
}
}
非静态方法
package oop;
public class Demo02 {
public static void main(String[] args) {
// 调用非静态方法
// 对象类型 对象名 = new 对象值
Student student = new Student();
student.say2();
}
}
形参和实参
值传递和引用传递
package oop;
// 值传递
public class Demo04 {
public static void main(String[] args) {
int a = 1;
new Demo04().change(a);
System.out.println(a); // 输出 a = 1,而非 a = 10
}
public void change(int a) {
a = 10;
}
}
package oop;
// 引用传递
public class Demo05 {
public static void main(String[] args) {
Person aaa = new Person();
System.out.println(aaa.name);// 输出 null
Demo05.change(aaa);
System.out.println(aaa.name);// 输出 狗蛋
}
public static void change(Person bbb) {
bbb.name = "狗蛋";
}
}
// 定义了一个 Person 类,有一个属性:name
class Person {
String name; // 默认初始值 null
}
this
关键字
类与对象的关系
类是一种抽象的数据类型,它是对某一类事物的整体描述 / 定义,但是并不能代表某一个具体的事物。
对象是抽象概念的具体实例
创建与初始化对象
使用new
关键字创建对象
使用new
关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认初始化,以及调用类中构造器。
类中构造器也称为构造方法,是创建对象时必须要调用的。构造器有以下特点:
void
package oop.demo;
// 一个项目应该只存在一个 main 方法
public class Application {
public static void main(String[] args) {
// 类:抽象的,实例化
// 类实例化后会返回一个自己的对象
// goudan 对象就是一个 Student 类的具体实例
Student goudan = new Student();
goudan.name = "狗蛋";
goudan.age = 3;
System.out.println(goudan.name);
System.out.println(goudan.age);
new Student().study();
}
}
package oop.demo;
// 学生类
public class Student {
// 属性:字段
String name;
int age;
// 方法
public void study() {
System.out.println(this.name + "在学习");
}
}
构造器:
作用:
new
(本质在调用构造方法)注意点:
定义有参构造后,若想使用无参构造,需显式地定义一个无参构造
构造快捷键 alt + ins
this. =
(this 代表本方法)
package oop.demo;
// 一个项目应该只存在一个 main 方法
public class Application {
public static void main(String[] args) {
// new 实例化了一个对象
// 1.使用 new 关键字,本质是在调用构造器
// 2.用来初始化值
Person person = new Person("狗蛋");
System.out.println(person.name);
}
}
package oop.demo;
public class Person {
// 一个类即使什么都不写,它也会存在一个方法
String name;
// 显式地定义无参构造器则如下
public Person() {
}
// 有参构造:一旦定义了有参构造,无参就必须显式定义
// 本质是 Person 方法的重载
public Person(String name) {
this.name = name;
}
// 快捷键 alt + insert 自动生成构造器
}
new
关键字创造对象(Person goudan = new Person();
)goudan.name
goudan.sleep()
该露的露,该藏的藏
封装(数据的隐藏)
属性私有,get
/ set
package oop.demo;
// 一个项目应该只存在一个 main 方法
public class Application {
public static void main(String[] args) {
Student s1 = new Student();
s1.setName("狗蛋");
System.out.println(s1.getName());
s1.setAge(18);
System.out.println(s1.getAge());
}
}
package oop.demo;
// 学生类
public class Student {
private String name;
private int age;
// alt + insert 快捷键,设置 setter & getter
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;
}
}
继承的本质是对某一批类的抽象,从而实现更好地建模
extends
(扩展),子类是父类的扩展
Java 中只有单继承,没有多继承
继承是类和类之间的一种关系。除此之外,类和类之间的关系还有依赖、组合、聚合等
继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extends
来表示。
子类和父类之间,从意义上讲应该具有 “is a” 的关系
package oop.demo;
public class Application {
public static void main(String[] args) {
Student s1 = new Student();
//Student 类已继承 Person 类的全部方法
s1.say();
}
}
public class Person {
// public
// protected
// default
// private
private int money = 10_0000_0000;
// Person 类的 say() 方法
public void say() {
System.out.println("说了一句话");
}
public void setMoney(int money) {
this.money = money;
}
public int getMoney() {
return money;
}
}
// extends 关键字,继承 Person 类的 say() 方法
public class Student extends Person {
}
super
详解super
调用父类的属性
package oop.demo;
public class Application {
public static void main(String[] args) {
Student s1 = new Student();
s1.test1("狗蛋"); // print "狗蛋" \n "学生" \n "人类"
}
}
package oop.demo;
public class Person {
protected String name = "人类";
}
package oop.demo;
// Student 类继承 Person 类
public class Student extends Person {
private String name = "学生";
public void test1(String name) {
System.out.println(name); // 局部变量 形式参数name("狗蛋")
System.out.println(this.name); // 实例变量 "学生"
System.out.println(super.name); // 实例变量 "人类"
}
}
super
调用父类的方法
package oop.demo;
public class Application {
public static void main(String[] args) {
Student s1 = new Student();
s1.test2(); // print "学生" \n "学生" \n "人类"
}
}
package oop.demo;
public class Person {
public void print() {
System.out.println("人类");
}
}
package oop.demo;
public class Student extends Person {
public void print() {
System.out.println("学生");
}
public void test2() {
print(); // "学生"
this.print(); // "学生"
super.print(); // "人类"
}
}
super
调用构造器(无参)
package oop.demo;
public class Application {
public static void main(String[] args) {
// 虽然只调用了 Student 类,但因为 Student 类 extends 了 Person 类,故会先生成父类 Person 的构造器,再生成子类 Student 的构造器
Student s1 = new Student(); // print "人类 无参" \n "学生 无参"
}
}
package oop.demo;
public class Person {
// 显式无参构造
public Person() {
System.out.println("人类 无参");
}
}
package oop.demo;
public class Student extends Person {
// 显式无参构造
public Student() {
// 隐藏代码 super(); ,调用了父类 Person 的无参构造。调用父类的构造器,必须要在子类构造器的第一行
System.out.println("学生 无参");
}
}
super
调用构造器(有参)
package oop.demo;
public class Application {
public static void main(String[] args) {
// 调用子类 有参构造器
Student s1 = new Student("狗蛋"); // print "人类" \n "狗蛋"
}
}
package oop.demo;
public class Person {
// 父类 有参构造器
public Person(String name) {
System.out.println(name);
}
}
package oop.demo;
public class Student extends Person {
// 子类 显式无参构造器
public Student() {
super("人类");
System.out.println("学生 无参");
}
// 子类 有参构造器
// 因父类构造器有参,必须用 super("String") 调用,否则报错
public Student(String name) {
super("人类");
System.out.println(name);
}
}
小结
super
注意点:
super
调用父类的构造方法,必须在构造方法的第一个super
只能出现在子类的方法或构造方法中super
和this
不能同时调用构造方法this
:
this
:本身调用者这个对象super
:代表父类对象的引用this
:没有继承也可以使用super
:只能在继承条件下使用this()
:本类的构造super()
:父类的构造静态方法调用
package oop.demo;
public class Application {
public static void main(String[] args) {
// 静态方法的调用只和左边定义的数据类型(B)有关
B b = new B();
b.test(); // print "B --> test()"
// 父类的引用指向了子类
A a = new B();
a.test(); // print "A --> test()"
}
}
package oop.demo;
public class A {
// static 静态方法
public static void test() {
System.out.println("A --> test()");
}
}
package oop.demo;
public class B extends A {
// static 静态方法
public static void test() {
System.out.println("B --> test()");
}
}
非静态方法重写
package oop.demo;
public class Application {
public static void main(String[] args) {
B b = new B();
b.test(); // print "B --> test()"
// 父类的引用指向了子类
A a = new B(); // 子类重写了父类的方法
a.test(); // print "B --> test()"
}
}
package oop.demo;
public class A {
// 非静态方法
public void test() {
System.out.println("A --> test()");
}
}
package oop.demo;
public class B extends A {
@Override // 注解:有功能的注释
// 非静态方法
public void test() {
System.out.println("B --> test()");
}
}
小结
private
< default
< protected
< public
override
即同一方法可以根据发送对象的不同而采用多种不同的行为方式
一个对象的实际类型是确定的,但可以指向对象的引用的类型有很多
多态是方法的多态,属性没有多态性
instanceof
类型转换(引用类型)
重写前的多态
package oop.demo;
public class Application {
public static void main(String[] args) {
Student s1 = new Student();
Person s2 = new Student();
Object s3 = new Student();
// 子类型 Student 的对象 s1 可以直接调用父类型 Person 的方法
s1.run(); // print "人类 跑"
s2.run(); // print "人类 跑"
}
}
package oop.demo;
public class Person {
public void run() {
System.out.println("人类 跑");
}
}
package oop.demo;
public class Student extends Person {
}
重写后的多态,拓展
package oop.demo;
public class Application {
public static void main(String[] args) {
Student s1 = new Student();
Person s2 = new Student(); // 父类的引用指向子类
Object s3 = new Student();
// run() 已被重写
s2.run(); // print "重写为:飞"
// s2.eat();报错:父类型 Person 的对象 s2 不能直接调用子类型 Student 的方法,需强制转换类型(Person 转 Student)
((Student) s2).eat(); // print "吃"
}
package oop.demo;
public class Person {
public void run() {
System.out.println("跑");
}
}
package oop.demo;
public class Student extends Person {
// 重写 run()
@Override
public void run() {
System.out.println("重写为:飞");
}
public void eat() {
System.out.println("吃");
}
}
instanceof
和类型转换instanceof
是 Java 的保留关键字,其作用是:测试左边的对象是否是右边的类的实例,返回boolean
的数据类型
比如 Person X = new Student()
,我们知道左边 Person 是引用类型,右边 Student 是实际类型
X instanceof Y
,若 X 的引用类型(Person)和 Y 有没有继承关系,则编译报错
若 X 的实际类型(Student)是 Y 本身,或 Y 的子类,则返回 true,否则返回 false
若 X 的实际类型是 Y 的父类,也返回 false
package oop.demo;
public class Application {
public static void main(String[] args) {
// Object > String
// Object > Person > Teacher
// Object > Person > Student
Object o = new Student();
System.out.println(o instanceof Student); // true
System.out.println(o instanceof Person); // true
System.out.println(o instanceof Object); // true
System.out.println(o instanceof Teacher); // false
System.out.println(o instanceof String); // false
Person p = new Student();
System.out.println(p instanceof Student); // true
System.out.println(p instanceof Person); // true
System.out.println(p instanceof Object); // true
System.out.println(p instanceof Teacher); // 引用类型 Person 和 Teacher 无继承关系,编译报错
System.out.println(p instanceof String); // 引用类型 Person 和 String 无继承关系,编译报错
Student s = new Student();
System.out.println(s instanceof Student); // true
System.out.println(s instanceof Person); // true
System.out.println(s instanceof Object); // true
System.out.println(s instanceof Teacher); // 引用类型 Student 和 Teacher 无继承关系,编译报错
System.out.println(s instanceof String); // 引用类型 Student 和 String 无继承关系,编译报错
}
}
package oop.demo;
public class Person {
}
package oop.demo;
public class Student extends Person {
}
package oop.demo;
public class Teacher {
}
基本类型不能用instanceof
判断
static
关键字详解静态属性
package oop.demo;
public class Student {
private static int age; // 静态变量
private double score; // 非静态变量
public static void main(String[] args) {
Student s1 = new Student();
System.out.println(Student.age);
System.out.println(Student.score); // 报错
System.out.println(s1.age);
System.out.println(s1.score);
}
}
静态方法
package oop.demo;
public class Student {
// 静态方法
public static void run() {
}
// 非静态方法
public void go() {
}
public static void main(String[] args) {
run();
go(); // 报错
}
}
静态代码块
package oop.demo;
public class Student {
// 执行顺序第 2 ,可用来赋初始值
{
System.out.println("匿名代码块"); // 代码块(匿名代码块)
}
// 执行顺序第 1 ,只执行 1 次
static {
System.out.println("静态代码块"); // 静态代码块
}
// 执行顺序第 3
public Student() {
System.out.println("构造方法"); // 构造器
}
public static void main(String[] args) {
Student s1 = new Student(); // print "静态代码块" \n "匿名代码块" \n "构造方法" \n
Student s2 = new Student(); // print "匿名代码块" \n "构造方法" \n
}
}
静态导入包
package oop.demo;
import static java.lang.Math.random; // 静态导入 Math.random() 方法
import static java.lang.Math.PI; // 静态导入 Math.PI 常量
public class Student {
public static void main(String[] args) {
System.out.println(random());
System.out.println(PI);
}
}
abstract
修饰符可以用来修饰方法(抽象方法),也可以用来修饰类(抽象类)。
抽象类中可以没有抽象方法,但有抽象方法的类一定要声明为抽象类。
抽象类不能使用new
关键字来创建对象,它是用来让子类继承的。
抽象方法只有声明,没有实现,它是用来让子类实现的。
子类继承抽象类,则必须要实现抽象类未实现的抽象方法,否则该子类也要声明为抽象类
package oop.demo;
public abstract class Person {
public abstract void doSomething(); // 抽象方法只有声明,没有方法体
}
package oop.demo;
public abstract class Student extends Person {
// 子类继承抽象类,重写实现抽象类未实现的抽象方法
@Override
public void doSomething() {
System.out.println("抽象类");
}
}
普通类:只有具体实现
抽象类:具体实现和规范(抽象方法)都有
接口:只有规范
接口的本质是契约
声明类的关键字是class
,声明接口的关键字是interface
package oop.demo;
public interface UserService {
// 接口中所有定义的属性其实都是静态常量 public static final
int AGE = 99;
// 接口中所有定义的方法其实都是抽象的 public abstract
void add(String name);
void delete(String name);
void update(String name);
void query(String name);
}
package oop.demo;
public interface TimeService {
void timer();
}
package oop.demo;
public class UserServiceImpl implements UserService, TimeService {
// 实现了接口的类,就需要重写接口中的方法
@Override
public void add(String name) {
}
@Override
public void delete(String name) {
}
@Override
public void update(String name) {
}
@Override
public void query(String name) {
}
@Override
public void timer() {
}
}
接口的作用:
public static final
常量public abstract
抽象implements
可以实现多个接口内部类就是在一个类的内部再定义一个类。 A 类中定义一个 B 类,则 B 是 A 的内部类,A 是 B 的外部类。
种类
成员内部类
创建成员内部类对象,必须先创建外部类对象,再通过外部类对象.new 构造内部类对象
成员内部类中不能有静态成员
成员内部类可以访问外部类的私有属性
package oop.demo;
public class Application {
public static void main(String[] args) {
Outer outer = new Outer();
outer.out(); // print "这是外部类的方法"
// 语法 outer.new Inner();
Outer.Inner inner = outer.new Inner();
inner.in(); // print "这是内部类的方法"
inner.getID(); // print 10
inner.getOut(); // print "这是外部类的方法"
}
}
package oop.demo;
// 外部类
public class Outer {
private int id = 10;
public void out() {
System.out.println("这是外部类的方法");
}
// 内部类
public class Inner {
public void in() {
System.out.println("这是内部类的方法");
}
// 可获得外部类的私有属性
public void getID() {
System.out.println(id); // print 10
}
// 可直接调用外部类的方法
public void getOut() {
out(); // print "这是外部类的方法"
}
}
}
静态内部类
静态内部类中只能访问外部类的静态成员
package oop.demo;
public class Application {
public static void main(String[] args) {
Outer outer = new Outer();
outer.out(); // print "这是外部类的方法"
// 语法 new Outer.Inner();
Outer.Inner inner = new Outer.Inner();
inner.in(); // print "这是内部类的方法"
}
}
package oop.demo
// 外部类
public class Outer {
public void out() {
System.out.println("这是外部类的方法");
}
// 静态内部类
public static class Inner {
public void in() {
System.out.println("这是内部类的方法");
}
}
}
局部内部类
局部内部类创建对象时,只能在定义它的方法内部进行
局部内部类可以去访问外部类的私有属性
局部内部类也可以去访问它所在方法的局部变量,但是要求局部变量必须被final修饰
jdk 8.0开始,默认为局部变量添加final
package oop.demo;
public class Application {
public static void main(String[] args) {
Outer outer = new Outer();
outer.out(); // print "这是外部类的方法" \n "这是外部类的方法"
}
}
package oop.demo;
public class Outer {
public void out() {
System.out.println("这是外部类的方法");
class Inner {
public void in() {
System.out.println("这是内部类的方法");
}
}
// 局部内部类创建对象,只能在 out() 方法内进行
Inner inner = new Inner();
inner.in();
}
}
匿名内部类
Error
和Exception
java.lang.Throwable
作为所有异常的超类Error
和异常Exception
Throwable
Error
VirtualMachineError
虚拟机运行错误
StacOverFlowError
栈溢出错误OutOfMemoryError
内存不足错误AWTError
Exception
IOExcption
EOFException
FileNotFoundException
找不到文件异常RuntimeException
运行时异常
ArrithmeticException
算术异常MissingResourceException
丢失资源ClassNotFoundException
找不到类NullPointerException
空指针异常IllegalArgumentException
不合法参数ArrayIndexOutOfBoundsException
数组下标越界UnknownTypeException
不明类型异常Error
:错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,编译中也检查不到。
Error
类对象由 Java 虚拟机生成并抛出,大多数错误与代码编写者所执行的操作无关VirtualMachineError
NoClassDefFoundError
,链接错误LinkageError
,这些错误是不可查的,因为它们在应用程序的控制和处理能力之外,而且绝大多数是程序运行时不允许出现的状况。Error
和Exception
的区别:Error
通常是灾难性的致命错误,是程序无法控制和处理的,当出现这些异常时,JVM 一般会选择终止线程。Exception
通常情况下是可以被程序处理的,并且在程序中应该尽可能地去处理这些异常。快捷键 Ctrl + Alt + T
抛出异常
捕获异常
异常处理 5 个关键字
try
、catch
、finally
、throw
、throws
package exception;
public class Demo01 {
public static void main(String[] args) {
int a = 1;
int b = 0;
// 假设要捕获多个异常:按照范围从小到大的顺序
try { // try 监控区域
System.out.println(a / b);
} catch (ArithmeticException e) { // catch 捕获异常
System.out.println("运行异常!除数不能为0!");
e.printStackTrace(); // 打印错误的栈信息
} catch (Error e) {
System.out.println("Error");
} catch (Exception e) {
System.out.println("Exception");
} catch (Throwable e) {
System.out.println("Throwable");
} finally { // 处理善后工作
System.out.println("finally");
}
}
}
package exception;
public class Demo02 {
public static void main(String[] args) {
Demo02 test = new Demo02();
test.test(1,0);
}
// 假设该方法中处理不了这个异常
// public void test(int a, int b) throws ArithmeticException() {
public void test(int a, int b) {
if (b == 0) {
// 主动抛出异常,一般在方法中使用
throw new ArithmeticException();
}
}
}
使用 Java 内置的异常类可以描述在编程时出现的大部分异常情况。除此之外,用户还可以自定义异常。用户自定义异常类,只需继承 Exception 类即可
创建自定义异常类
在方法中通过throw
关键字抛出异常对象
如果在当前抛出异常的方法中处理异常,可以使用try
-catch
语句捕获并处理(程序继续正常运行);否则在方法的声明处通过throws
关键字指明要抛出给方法调用者的异常(程序中断不继续运行)
在出现异常方法的调用者中捕获并处理异常
package exception;
// 自定义的异常类
public class MyException extends Exception{
// 传递数字 >10
private int detail;
public MyException(int a) {
this.detail = a;
}
// toString:异常的打印信息
@Override
public String toString() {
return "MyException{" + "detail=" + detail + '}';
}
}
package exception;
public class Test {
// 可能会存在异常的方法,`throws`关键字抛出
static void test(int a) throws MyException {
System.out.println("传递的参数为:" + a);
if (a > 10) {
throw new MyException(a);
}
System.out.println("OK");
}
public static void main(String[] args) {
// `try`-`catch`关键字捕获
try {
test(11);
} catch (MyException e) {
System.out.println("自定义异常:" + e);
// print 传递的参数为:11 \n 自定义异常:MyException{detail=11}
}
}
}
实际应用中的经验总结
try
-catch
处理catch
块后面,可以加一个尽可能大的catch (Exception e)
来处理可能会被遗漏的异常try
-catch
,处理潜在的异常printStackTrace()
去打印输出finally
语句块去释放占用的资源