前言
本篇是老师布置的一个作业,发表一篇基础语法知识点梳理与总结的个人笔记,如有错误或遗漏,烦请各位大佬指出
1. 标识符
1.1 概念
1. Java 对各种变量,方法和类等命名时使用的字符序列成为标识符
2. 凡是自己可以起名字的地方都叫标识符 如:int num1 = 90;
1.2 命名规范
1. 由26个英文字母大小写,0-9,_或$组成。
2. 数字不可以开头。 如 int 3ab = 1; //错误
3. 不可以使用关键字或保留字,但可以包含关键字和保留字。
关键字:
保留字:goto、const
4.Java中严格区分大小写,长度无限制。
5. 标识符不能包含空格。
2 运算符
介绍:
运算符是一种特殊的符号,用以表示数据的运算、赋值和比较等。包括下面6种运算符
1) 算术运算符
2) 赋值运算符
3) 关系运算符 [比较运算符]
4) 逻辑运算符
5) 位运算符 [需要二进制基础]
6) 三元运算符
运算符优先级
2.1 算术运算符
2.1.1 算术运算符介绍
算术运算符是对数值类型的变量进行运算,在Java程序中使用的非常多。
2.1.2 算术运算符一览
2.1.1 关于自增++和自减--的使用
作为独立语句使用时:
i++ 和++i 都等价于i = i +1
作为表达式使用时:
前++: ++i 先自增后赋值
后++: i++先赋值后自增
自减--同理
public class test {
public static void main(String[] args) {
int i = 10;
int a = ++i; //i先自增后在赋值给a
System.out.println(i); //输出 11
System.out.println(a); //输出11
int j = 10;
int b = j++; //先赋值在自增
System.out.println(j); //输出11
System.out.println(b); //输出10
}
}
2.2关系运算符
2.2.1 关系运算符介绍
1. 关系运算符的结果都是 boolean 型,要么是true,要么是false
2. 关系表达式经常用在 if 结构的条件中或 while 循环结构的条件中
2.2.2 关系运算符一览
2.3 逻辑运算符
2.3.1 逻辑运算符介绍
用于连接多个条件(多个关系表达式),最终的结果也是一个 boolean 值。
2.3.2 逻辑运算符一览
分为两组
1) 短路与 && , 短路或 ||,取反 !
2) 逻辑与 &,逻辑或 |,^ 逻辑异或
2.3.3 逻辑运算规则:
1) a&b : & 叫逻辑与:规则:当 a 和 b 同时为 true ,则结果为 true, 否则为 false
2) a&&b : && 叫短路与:规则:当 a 和 b 同时为 true ,则结果为 true,否则为 false
3) a|b : | 叫逻辑或,规则:当 a 和 b ,有一个为 true ,则结果为 true,否则为 false
4) a||b : || 叫短路或,规则:当 a 和 b ,有一个为 true ,则结果为 true,否则为 false
5) !a : 叫取反,或者非运算。当 a 为 true, 则结果为 false, 当 a 为 false 是,结果为 true
6) a^b: 叫逻辑异或,当 a 和 b 不同时,则结果为 true, 否则为 false
逻辑与 & 和 短路与 && 的区别
1) &&短路与:如果第一个条件为 false,则第二个条件不会判断,最终结果为 false,效率高
2) & 逻辑与:不管第一个条件是否为 false,第二个条件都要判断,效率低
逻辑或 | 和 短路或 || 的区别
1) || 短路或:如果第一个条件为 true,则第二个条件不会判断,最终结果为 true,效率高
2) | 逻辑或:不管第一个条件是否为 true,第二个条件都要判断,效率低
2.4 赋值运算符
2.4.1 赋值运算符介绍
赋值运算符就是将某个运算后的值,赋给指定的变量
2.4.2 赋值运算符一览
2.5 三元运算符
2.5.1 基本语法
条件表达式 ? 表达式1 : 表达式2;
运算规则:
1.如果条件表达式为true,运算后的结果是表达式1
2. 如果条件表达式为false,运算后的结果是表达式2
2.5.2 使用细节
1) 表达式1和表达式2腰围可以赋给接受变量的类型(或可以自动转换)
2) 三元运算符可以转为 if--else 语句
int res = a > b ? a++ : --b;
if ( a > b) res = a++;
else res = --b;
2.6 位运算符
java中有7个位运算(&、|、^ 、~、>>、<<、>>>)
按位与 & : 两位全为1,结果为1,否则为0
按位或 | : 两位有一个为1,结果为1,否则为0
按位异或 ^: 两位一个为1,一个为0,结果为1,否则为0
按位取反 ~: 0→1,1→0
算术右移 >>: 低位溢出,符号位不变,并用符号位补溢出的高位
算数左移 <<: 符号位不变,低位补0
无符号右移 >>>: 低位溢出,高位补0
3. 程序流程控制
3.1 程序流程控制介绍
在程序中,程序运行的流程控制决定程序是如何执行的,是我们必须掌握的,主要有三大流程控制语句。
1) 顺序控制
2) 分支控制
3) 循环控制
3.2 顺序控制
程序从上到下逐条地执行,中间没有任何判断和跳转。
3.3 分支控制 if-else
3.3.1 分支控制 if-else 介绍
让程序有选择的执行,分支控制有3种
1) 单分支 if
2) 双分支 if-else
3) 多分支 if-else if -......-else
3.3.2单分支
基本语法:
if(条件表达式){
执行代码块;(可以有多条语句)
}
说明: 当条件表达式为true 时,就会执行大括号{ }里的代码。如果为false,就不执行
(注:如果大括号{ }中只有一条语句,则可以不用{ },建议还是写上)
案例:
Scanner myScanner = new Scanner(System.in);
System.out.println("请输入年龄");
//把年龄保存到一个变量 int age
int age = myScanner.nextInt();
//使用 if 判断,输出对应信息
if (age > 18) {
System.out.println("你年龄大于 18,要对自己的行为负责");
}
System.out.println("程序继续...");
3.3.3 双分支
基本语法:
if(条件表达式){
执行代码块1;
}
else{
执行代码块2;
}
说明:当条件表达式成立时,即执行代码块1,否则执行代码块2
(注:如果大括号{ }中只有一条语句,则可以不用{ },建议还是写上)
Scanner myScanner = new Scanner(System.in);
System.out.println("请输入年龄");
//把年龄保存到一个变量 int age
int age = myScanner.nextInt();
//使用 if 判断,输出对应信息
if (age > 18) {
System.out.println("你年龄大于 18,要对自己的行为负责");
}else {
System.out.println("年龄不够,放你一马");
}
3.3.4 多分支
基本语法:
if(条件表达式){
执行代码块1;
}else if{
执行代码块2;
}.......
else{
执行代码块n;
}
说明:(1)多分支可没有else,如果所有的条件表达式都不成立,则一个执行入口都没有
(2)如果有else,如果所有的条件表达式都不成立,则默认执行else代码块
案例:
Scanner myScanner = new Scanner(System.in);
int grade = myScanner.nextInt();
//先对输入的信用分,进行一个范围的有效判断 1-100, 否则提示输入错误
if(grade >=1 && grade <= 100) {
//因为有 4 种情况,所以使用多分支
if(grade == 100) {
System.out.println("信用极好");
} else if (grade > 80 && grade <= 99) { //信用分为(80,99]时,输出 信用优秀;
System.out.println("信用优秀");
} else if (grade >= 60 && grade <= 80) {//信用分为[60,80]时,输出 信用一般
System.out.println("信用一般");
} else {//其它情况 ,输出 信用 不及格
System.out.println("信用不及格");
}
} else {
System.out.println("信用分需要在 1-100,请重新输入:)");
}
3.3.5 嵌套分支
在一个分支结构中又完整嵌套了另一个完整的分支结构(内层嵌套尽量不要过多,否则可读性不好)
//基本语法
if(){
if(){
//if else
}else{
//if else
}
}
案例:
Scanner myScanner = new Scanner(System.in);
System.out.println("请输入该歌手的成绩");
double score = myScanner.nextDouble();
if (score > 8.0) {
System.out.println("请输入性别");
char gender = myScanner.next().charAt(0);
if (gender == '男') {
System.out.println("进入男子组");
} else if (gender == '女') {
System.out.println("进入女子组");
} else {
System.out.println("你的性别有误,不能参加决赛~");
}
} else {
System.out.println("sorry ,你被淘汰了~");
}
3.3.6 switch 分支结构
基本语法
基本语法:
switch (表达式){
case 常量1:
语句块1;
break;
case 常量2:
语句块2;
break;
case 常量n:
语句块n;
break;
default:
default语句块;
break;
}
1. switch关键字,表示switch分支
2. 表达式对应一个值
3. case 常量1:表示当表达式的值等于常量1,就执行语句块1
4. break:表示退出switch
5. 如果和case 常量1 匹配,就执行语句块1,如果没有匹配,就继续匹配 case 常量2
6. 如果一个都没有匹配上,执行default
一些注意事项
1. 表达式数据类型,应和case 后的常量类型一致,或者是可以自动转成可以相互比较的类型,比如输入的是字符,而常量是int,则是错误,会抛出异常
2. switch(表达式)中表达式的返回值必须是:( byte , short , int , char , enum(枚举), String)
3. case子句中的值必须是常量,而不能是变量
4. default子句是可选的,当没有匹配的case是,执行default
5. break语句用来执行完一个case分支后使程序跳出switch语句块,如果没有写break,程序会顺序执行到switch结尾,除非遇到break
3.4 循环控制
听起名而知其意,就是让你的代码可以循环的执行
3.4.1 for循环
基本语法:
for(循环变量初始化;循环条件;循环变量迭代){
循环操作(可以多条语句);
}
1. for 关键字,表示循环控制
2. for 有四要素: (1)循环变量初始化(2)循环条件(3)循环操作(4)循环变量迭代
3. 循环操作 , 这里可以有多条语句,也就是我们要循环执行的代码
4. 如果 循环操作(语句) 只有一条语句,可以省略 {}, 建议不要省略
一些注意事项
1) 循环条件是返回一个布尔值的表达式
2) for(;循环判断条件;) 中的初始化和变量迭代可以写到其它地方,但是两边的分号不能省略。
3) 循环初始值可以有多条初始化语句,但要求类型一样,并且中间用逗号隔开,循环变量迭代也可以有多条变量迭代 语句,中间用逗号隔开。
案例:
//打印 1~100 之间所有是 9 的倍数的整数,统计个数及总和
int count = 0; //统计 9 的倍数个数 变量
int sum = 0; //总和
int start = 10;
int end = 200;
int t = 5; // 倍数
for(int i = start; i <= end; i++) {
if( i % t == 0) {
System.out.println("i=" + i);
count++;
sum += i;//累积
}
}
System.out.println("count=" + count);
System.out.println("sum=" + sum);
3.4.2 while循环
基本语法:
循环变量初始化
while(循环条件){
循环体(语句);
循环变量迭代;
}
while循环也有四要素,只是四要素放的位置和for不一样
int i = 1; //循环变量初始化
while( i <= 10 ) {//循环条件
System.out.println("你好" + i);//执行语句
i++;//循环变量迭代
}
一些注意事项
1) 循环条件是返回一个布尔值的表达式
2) while 循环是先判断再执行语句
3.4.3 do while循环
基本语法:
循环变量初始化
do{
循环体(语句);
循环变量迭代;
}while(循环条件);
1. do while 是关键字
2. 也有循环四要素, 只是位置不一样
3. 先执行,再判断,也就是说,一定会至少执行一次
4. 最后 有一个 分号
案例
int i = 1; //循环变量初始化
do {
System.out.println("你好" + i);//执行语句
i++;//循环变量迭代
} while( i <= 10 );//循环条件
一些注意事项
1) 循环条件是返回一个布尔值的表达式
2) do..while 循环是先执行,再判断, 因此它至少执行一次
3.4.4 多重循环
1) 将一个循环放在另一个循环体内,就形成了嵌套循环。其中,for ,while ,do…while 均可以作为外层循环和内层循环。 【建议一般使用两层,最多不要超过 3 层, 否则,代码的可读性很差】
2) 实质上,嵌套循环就是把内层循环当成外层循环的循环体。当只有内层循环的循环条件为 false 时,才会完全跳出内 层循环,才可结束外层的当次循环,开始下一次的循环。
3) 设外层循环次数为 m 次,内层为 n 次,则内层循环体实际上需要执行 m*n
案例:
打印九九乘法表
for(int i = 1; i <= 9; i++){
for(int j = 1; j <= i; j++){
System.out.print(+ i + "×" + j + "=" + (i*j) + "\t");
}
System.out.println(); //换行
}
3.4.5 跳转控制语句
break
break 语句用于终止某个语句块的执行,一般使用在 switch 或者循环[for , while , do-while]中
基本语法:
{
……
break;
……
}
continue
continue 语句用于结束本次循环,继续执行下一次循环。
{
......
continue;
......
}
4. 数组
数组可以存放多个同一类型的数据。数组也是一种数据类型,是引用类型。
使用方法
声明数组: 要声明一个数组,需要指定数组的类型、名称和可选的数组维度。
int[] intArray; // 声明一个整数数组
创建数组: 声明数组后,需要使用new
关键字来为数组分配内存空间,并指定数组的大小
intArray = new int[5]; // 创建一个包含5个整数的数组
初始化数组: 可以使用大括号 {}
来初始化数组元素的值
int[] intArray = {1, 2, 3, 4, 5}; // 初始化整数数组
访问数组元素: 数组元素的访问是通过索引来完成的,索引从0开始计数
int firstElement = intArray[0];
多维数组: Java支持多维数组,可以创建二维、三维或更高维的数组。多维数组的声明和创建方式如下:
int[][] twoDArray = new int[3][4]; // 创建一个3x4的二维整数数组
遍历数组:使用循环结构(如for
循环或foreach
循环)可以遍历数组中的元素
int[] arr = {1, 2, 3, 4, 5};
for (int i = 0; i < arr.length; i++) {
// 使用arr[i]访问数组元素
}
for (int element : arr) {
// 使用element访问数组元素,这是foreach循环
}
修改数组的元素:
public class ModifyArrayExample {
public static void main(String[] args) {
// 创建一个整数数组
int[] numbers = {1, 2, 3, 4, 5};
// 修改数组中的值
numbers[2] = 99; // 将第三个元素(索引2)的值修改为99
// 打印修改后的数组
for (int i = 0; i < numbers.length; i++) {
System.out.println("Element at index " + i + ": " + numbers[i]);
}
}
}
数组的一些常用方法
1. 数组的长度:使用数组的length
属性可以获取数组中元素的数量
int[] arr = {1, 2, 3, 4, 5};
int length = arr.length; // 获取数组长度,length的值为5
2. 数组拷贝: Arrays.copyOf
方法可以将一个数组的内容复制到另一个数组。
int[] sourceArray = {1, 2, 3};
int[] targetArray = new int[3];
targetArray = Arrays.copyOf(sourceArray, sourceArray.length);
3. 数组排序:使用Arrays.sort
方法可以对数组进行升序排序。
int[] arr = {5, 1, 3, 2, 4};
Arrays.sort(arr); // 对数组进行升序排序
4. 数组搜索:使用Arrays.binarySearch
方法可以在已排序的数组中执行二分查找。
int[] arr = {1, 2, 3, 4, 5};
int index = Arrays.binarySearch(arr, 3); // 查找元素3的索引,返回2
5. 数组填充:使用Arrays.fill
方法可以将数组的所有元素设置为指定的值。
int[] arr = new int[5];
Arrays.fill(arr, 0); // 将数组所有元素设置为0
6. 数组转换为字符串:使用Arrays.toString
方法可以将数组转换为字符串,便于打印或显示。
int[] arr = {1, 2, 3, 4, 5};
String arrString = Arrays.toString(arr); // 将数组转换为字符串
System.out.println(arrString); // 打印数组内容
7. 复制数组:使用Arrays.copyOf
或Arrays.copyOfRange
方法可以复制数组的一部分或全部。
int[] sourceArray = {1, 2, 3, 4, 5};
int[] copyArray = Arrays.copyOf(sourceArray, sourceArray.length); // 复制整个数组
int[] partialCopy = Arrays.copyOfRange(sourceArray, 1, 4); // 复制索引1到3的部分
8. 判断数组相等: Arrays.equals(array1, array2);
判断两个数组是否相等。
使用注意事项
1) 数组是多个相同类型数据的组合,实现对这些数据的统一管理
2) 数组中的元素可以是任何数据类型,包括基本类型和引用类型,但是不能混用。
3) 数组创建后,如果没有赋值,有默认值 int 0,short 0, byte 0, long 0, float 0.0,double 0.0,char \u0000,boolean false,String null
4) 使用数组的步骤 1. 声明数组并开辟空间 2 给数组各个元素赋值 3 使用数组
5) 数组的下标是从 0 开始的。
6) 数组下标必须在指定范围内使用,否则报:下标越界异常
7) 数组属引用类型,数组型数据是对象(object)
5. 面向对象
5.1 类与对象
知识点
类和对象是面向对象编程的核心概念,它们提供了一种将数据和行为组织成独立单元的方式,从而更容易理解和维护代码。通过创建类和对象,您可以将现实世界的实体抽象为可编程实体,使代码更模块化、可扩展和可维护。
类(Class):
-
类的定义:
- 类是一种模板,用于描述对象的属性(成员变量)和行为(成员方法)。
- 类定义了对象的结构和行为,是创建对象的蓝图。
-
成员变量:
- 成员变量(字段)是类的属性,用于存储对象的状态信息。
- 成员变量通常是私有的,可以通过公有方法访问或修改。
-
成员方法:
- 成员方法定义了类的行为,用于执行特定的操作。
- 成员方法可以访问成员变量并与它们交互,也可以接受参数和返回值。
-
封装:
- 封装是一种面向对象编程的原则,它将类的内部细节隐藏起来,仅暴露必要的接口。
- 封装有助于维护对象的数据完整性和安全性。
-
构造方法:
- 构造方法是一种特殊的成员方法,用于初始化对象的状态。
- 构造方法的名称与类名相同,不返回任何值。
对象(Object):
-
对象的创建:
- 对象是类的实例,通过关键字
new
创建。 - 创建对象后,分配内存并调用类的构造方法来初始化对象。
- 对象是类的实例,通过关键字
-
访问成员变量:
- 对象可以访问和修改类中的成员变量。
- 成员变量的访问通常通过对象的方法来实现。
-
调用成员方法:
- 对象可以调用类中定义的成员方法。
- 成员方法的调用通常通过对象来执行,可以传递参数并获取返回值。
-
多个对象:
- 可以创建多个类的不同对象,它们具有相同的类定义,但可以有不同的状态和行为。
- 每个对象是独立的实体,不会相互干扰。
-
引用:
- 对象可以通过引用变量来引用,引用变量指向对象的内存地址。
- 多个引用变量可以引用同一个对象。
-
垃圾回收:
- Java 自动管理对象的生命周期,当对象不再被引用时,垃圾回收机制将释放其内存。
- 这有助于避免内存泄漏。
示例:
// 定义一个类
class Person {
// 成员变量
private String name;
private int age;
// 构造方法
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 成员方法
public void sayHello() {
System.out.println("Hello, my name is " + name + " and I'm " + age + " years old.");
}
}
public class Main {
public static void main(String[] args) {
// 创建对象
Person person1 = new Person("Alice", 25);
Person person2 = new Person("Bob", 30);
// 访问成员变量和调用成员方法
System.out.println(person1.name); // 编译错误,因为name是私有的
System.out.println(person1.age); // 编译错误,因为age是私有的
person1.sayHello(); // 输出:Hello, my name is Alice and I'm 25 years old.
person2.sayHello(); // 输出:Hello, my name is Bob and I'm 30 years old.
}
}
访问修饰符
访问修饰符是用于控制类、成员变量和方法的可见性和访问权限的关键概念。它定义了哪些部分的代码可以访问某个类、成员变量或方法。Java 中有多个访问修饰符,包括以下几种:
-
public
:public
是最宽松的访问修饰符,它表示任何类、任何包中的类,以及任何地方的代码都可以访问被public
修饰的类、成员变量或方法。
-
private
:private
是最严格的访问修饰符,它表示只有同一类内部的代码可以访问被private
修饰的类、成员变量或方法。private
通常用于封装内部实现细节。
-
protected
:protected
表示只有同一类、同一包中的类,或子类可以访问被protected
修饰的成员变量或方法。protected
通常用于实现继承和封装。
-
默认(无修饰符):
- 如果没有使用任何访问修饰符,称为“默认”或“包级私有”访问修饰符。
- 默认访问级别表示只有同一包中的类可以访问被默认修饰的类、成员变量或方法。
this
this
是一个关键字,用于引用当前对象,通常用于以下几种情况:
-
解决变量名冲突:
- 当局部变量和实例变量具有相同的名称时,使用
this
关键字来区分它们。 - 这有助于编译器明确哪个变量是局部变量,哪个是实例变量。
- 当局部变量和实例变量具有相同的名称时,使用
-
在构造方法中调用其他构造方法:
- 在一个类的构造方法中,可以使用
this
来调用同一个类的其他构造方法,以避免代码重复。 - 这种方式通常用于构造方法的重载。
- 在一个类的构造方法中,可以使用
示例:
public class Person {
private String name;
private int age;
// 构造方法1
public Person(String name) {
this.name = name; // 使用this关键字引用实例变量
}
// 构造方法2,调用构造方法1
public Person(String name, int age) {
this(name); // 调用构造方法1,避免代码重复
this.age = age; // 使用this关键字引用实例变量
}
public void printInfo() {
System.out.println("Name: " + this.name); // 使用this关键字引用实例变量
System.out.println("Age: " + age); // this关键字可以省略
}
public static void main(String[] args) {
Person person = new Person("Alice", 25);
person.printInfo();
}
}
方法重载
方法重载(Method Overloading)是一种在同一个类中定义多个具有相同名称但不同参数列表的方法的技术。方法重载允许使用相同的方法名来执行不同的操作,根据传递给方法的参数数量、类型或顺序来区分不同的重载版本。
方法重载的知识点:
-
方法签名:
- 方法签名包括方法的名称、返回类型和参数列表。
- 两个方法的方法签名必须不同,否则它们无法构成方法重载。
-
方法参数:
- 方法重载依赖于方法的参数列表。
- 参数可以有不同的数量、不同的类型,或两者同时不同,以便区分不同的重载版本。
-
返回类型:
- 方法重载的方法返回类型可以相同也可以不同。
- 返回类型不是区分方法重载的标准。
-
方法重载的使用场景:
- 提供了相似的操作,但需要不同的输入参数。
- 提供了不同的参数类型,以便适应不同的用例。
- 使方法名称更直观和易于记忆,以提高代码的可读性
(注:
- 方法名必须相同
- 参数列表:必须不同(参数类型或个数或程序,至少有一样不一样,参数名无要求))
示例:
public 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;
}
// 方法重载:两个双精度浮点数相加
public double add(double a, double b) {
return a + b;
}
public static void main(String[] args) {
Calculator calculator = new Calculator();
int sum1 = calculator.add(5, 10);
int sum2 = calculator.add(5, 10, 15);
double sum3 = calculator.add(2.5, 3.7);
System.out.println("Sum1: " + sum1); // 输出:Sum1: 15
System.out.println("Sum2: " + sum2); // 输出:Sum2: 30
System.out.println("Sum3: " + sum3); // 输出:Sum3: 6.2
}
}
构造器
构造器(Constructor)是一种特殊类型的方法,用于初始化对象的状态。构造器在对象创建时自动调用,它负责执行一些初始化操作,例如设置对象的初始属性值。以下是关于构造器的知识点以及一个简单的 Java 示例:
构造器的知识点:
-
构造器名称:
- 构造器的名称与类名称相同。
- 构造器不返回任何值,甚至没有
void
关键字。
-
构造器重载:
- 可以在同一个类中定义多个构造器,根据不同的参数列表来实现构造器重载。
- 构造器重载允许对象以不同的方式初始化。
-
默认构造器:
- 如果类没有显式定义任何构造器,Java 会提供一个默认构造器,它不带参数并执行空操作。
-
显式构造器:
- 如果类定义了至少一个构造器,Java 不会自动提供默认构造器。
- 如果需要使用默认构造器,必须显式定义它。
-
this 关键字:
- 构造器中可以使用
this
关键字来调用类中的其他构造器,以避免代码重复。
- 构造器中可以使用
示例:
public class Person {
private String name;
private int age;
// 构造器1:带参数
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 构造器2:不带参数
public Person() {
this.name = "Unknown";
this.age = 0;
}
public void printInfo() {
System.out.println("Name: " + name);
System.out.println("Age: " + age);
}
public static void main(String[] args) {
Person person1 = new Person("Alice", 25);
Person person2 = new Person();
person1.printInfo();
person2.printInfo();
}
}
作用域
在面向对象编程(OOP)中,作用域的概念与其他编程范式类似,但在类和对象的上下文中有一些特定的考虑。以下是与面向对象编程中的作用域相关的知识点:
-
实例变量作用域:
- 实例变量是存储在对象内部的变量,其作用域是整个对象。
- 实例变量可以通过对象引用来访问,并且在对象的生命周期内一直存在。
-
方法作用域:
- 方法内的局部变量的作用域仅限于该方法。
- 方法参数也是局部变量,它们的作用域也限定在方法内。
- 方法内的局部变量只在方法被调用时存在,一旦方法执行完成,它们就会被销毁。
-
类作用域:
- 类级别的变量,例如静态成员变量,具有类作用域。
- 这些变量对于整个类的所有对象都是可见的,它们存在于整个类的生命周期中。
-
成员访问权限:
- 在面向对象编程中,不同的访问修饰符(
public
、private
、protected
、默认)控制了类成员的可见性和访问权限。 public
成员可以被类的外部访问,而private
成员只能在类内部访问。protected
成员对于子类也是可见的,而默认访问级别成员只能在同一包中访问。
- 在面向对象编程中,不同的访问修饰符(
-
对象作用域:
- 每个对象都有自己的作用域,其中包含其成员变量的值。
- 对象的作用域与对象的生命周期密切相关。对象创建时,它的成员变量的作用域开始,对象销毁时,它的作用域结束。
-
继承和作用域:
- 子类可以继承父类的成员变量和方法,但访问权限仍然受到控制。
- 子类可以访问继承的
public
和protected
成员,但不能直接访问父类的private
成员。
包
在Java中,包(Package)是一种用于组织和管理类和接口的命名空间。包可以将相关的类和接口组织在一起,以便更好地管理和维护代码。以下是有关包的知识点:
-
包的定义:
- 包是一个包含类和接口的目录结构,它通常与文件系统中的目录层次结构相对应。
- 包名是一个唯一的标识符,通常使用小写字母。
-
包的作用:
- 组织:包帮助将类和接口组织成逻辑单元,提高代码的可维护性和可读性。
- 命名空间:包提供了一种命名空间,可以避免类名的冲突。
- 访问控制:包可以用于控制类的可见性,通过访问修饰符(
public
、protected
、private
)定义哪些类可以被其他包中的类访问。
-
包的声明:
- 在Java源文件的开头,使用
package
关键字声明包的名称。 - 包的声明必须是文件的第一行,只能出现一次。
- 在Java源文件的开头,使用
package com.example.myapp;
4.包的导入:
- 可以使用
import
关键字来导入其他包中的类或接口,以便在当前类中使用它们。 - 可以导入单个类、整个包或静态成员。
import java.util.ArrayList;
import java.util.*; // 导入java.util包中的所有类
import static com.example.myapp.MyClass.*; // 导入MyClass类的所有静态成员
5.默认包:
- 如果没有声明包,类将属于默认包,其名称是空字符串。
- 默认包中的类只能被同一个包中的其他类访问。
6.包的命名规范:
- 包名通常使用小写字母,可以包含数字和下划线。
- 按照惯例,包名通常采用反转的域名格式,以确保唯一性,例如
com.example.myapp
。
5.2 封装
封装(Encapsulation)是面向对象编程(OOP)的一个重要原则,它指的是将对象的内部状态和行为隐藏在对象的外部,只暴露必要的接口。封装有助于保护对象的数据完整性,提高代码的可维护性和可扩展性。以下是关于封装的知识点:
-
私有成员变量:
- 封装通过将类的成员变量标记为私有(private)来开始。
- 私有成员变量只能在类的内部访问,外部代码无法直接访问它们。
-
公有方法:
- 封装提供了公有(public)方法,用于允许外部代码访问和修改私有成员变量。
- 这些方法充当了对象与外部世界之间的接口。
-
getter 和 setter 方法:
- getter 方法用于获取私有成员变量的值。
- setter 方法用于设置私有成员变量的值。
- getter 和 setter 方法通常遵循命名约定,例如
getVariableName
和setVariableName
。
-
数据封装:
- 数据封装是一种将数据和处理数据的方法封装在一起的概念。
- 这有助于隐藏数据的实现细节,并确保数据的完整性。
-
访问控制:
- 封装允许开发人员控制对类的成员变量的访问权限。
- 可以使用访问修饰符(如
private
、protected
、public
)来定义成员变量的可见性。
-
封装的优点:
- 提高代码的可维护性:外部代码不需要了解对象的内部实现细节,只需使用公有接口。
- 保护数据的完整性:通过限制对数据的直接访问,可以确保数据不会被无效或恶意修改。
- 支持代码重构:封装使得更容易对类的内部实现进行修改,而不影响外部代码。
示例:
public class Person {
// 私有成员变量
private String name;
private int age;
// 构造方法
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 公有 getter 方法
public String getName() {
return name;
}
// 公有 setter 方法
public void setName(String name) {
this.name = name;
}
// 公有 getter 方法
public int getAge() {
return age;
}
// 公有 setter 方法
public void setAge(int age) {
if (age >= 0) {
this.age = age;
}
}
}
5.3 继承
知识点
继承是面向对象编程的一个重要概念,它允许一个类(子类/派生类)继承另一个类(父类/基类)的属性和方法。继承通过创建一个新类,该类从现有类继承属性和方法,以实现代码重用和扩展。以下是关于继承的知识点以及一个简单的 Java 示例:
-
父类和子类:
- 父类是提供继承的类,也称为基类。
- 子类是继承属性和方法的类,也称为派生类。
- 子类可以继承父类的公有和受保护成员。
-
extends 关键字:
- 在 Java 中,使用
extends
关键字来建立继承关系。 - 子类使用
extends
后跟父类的名称来声明继承关系。
- 在 Java 中,使用
-
方法重写:
- 子类可以重写父类的方法,以改变或扩展其行为。
- 重写的方法使用
@Override
注解来标记。
-
super 关键字:
- 子类可以使用
super
关键字来调用父类的构造方法或成员方法。 super
用于区分子类和父类的成员变量和方法。
- 子类可以使用
-
构造方法的调用:
- 子类的构造方法可以通过
super()
调用父类的构造方法,以初始化继承的成员变量。
- 子类的构造方法可以通过
-
多层继承:
- 在 Java 中,可以创建多层继承关系,即子类可以有子类。
- 多层继承可以用来实现更丰富的类层次结构。
示例:
// 父类
class Animal {
void eat() {
System.out.println("Animal is eating.");
}
}
// 子类继承父类
class Dog extends Animal {
// 子类可以重写父类的方法
@Override
void eat() {
System.out.println("Dog is eating.");
}
void bark() {
System.out.println("Dog is barking.");
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.eat(); // 输出:Dog is eating.
dog.bark(); // 输出:Dog is barking.
}
}
super
知识点
super
是一个关键字,在面向对象编程中,它用于引用父类的成员变量、构造方法和方法。super
主要用于以下几种情况:
-
访问父类的成员变量:
super
可以在子类中用来访问父类的成员变量,尤其在子类中有同名的成员变量时,可以使用super
来区分。
-
调用父类的构造方法:
- 在子类的构造方法中,可以使用
super
来调用父类的构造方法,以初始化继承的成员变量。
- 在子类的构造方法中,可以使用
-
调用父类的方法:
super
可以在子类中用来调用父类的方法,尤其在子类中重写了父类的方法时,可以使用super
来调用父类的版本。
-
避免歧义:
- 在多层继承关系中,如果有多个父类,使用
super
可以明确指定调用哪个父类的方法或构造方法。
- 在多层继承关系中,如果有多个父类,使用
和this的区别
super
和 this
是两个关键字,用于不同的上下文,具有不同的用途。以下是它们之间的主要区别:
-
引用对象:
super
用于引用父类的成员(方法、变量、构造方法),以便访问或调用它们。this
用于引用当前对象的成员,即当前类的成员。
-
使用地点:
super
可以在子类中使用,用于引用父类的成员。this
可以在类的方法中使用,用于引用当前对象的成员。
-
调用构造方法:
super()
用于在子类的构造方法中调用父类的构造方法,以初始化继承的成员变量。this()
用于在一个构造方法中调用同一个类的其他构造方法,以避免代码重复。
-
区分同名成员:
super
可以用于在子类中访问父类和子类中同名的成员,以区分它们。this
可以用于在当前类中访问同名的成员,以区分局部变量和成员变量。
-
目的:
super
用于与父类的成员进行交互,或在子类中扩展父类的功能。this
用于在当前对象中引用自身的成员,或在构造方法中调用其他构造方法。
方法重写
知识点
方法重写(Method Overriding)是面向对象编程中的一个重要概念,它允许子类重写父类中已有的方法,以提供自己的实现。方法重写用于实现多态性,使得不同子类可以有不同的行为,同时保持方法的名称和参数列表相同。以下是关于方法重写的知识点:
-
方法重写的要求:
- 子类的重写方法的名称、返回类型和参数列表必须与父类中被重写的方法完全相同。
- 子类重写的方法不能拥有比父类方法更严格的访问修饰符(即,可以放宽,但不能缩小访问权限)。
- 如果父类方法被标记为
final
(无法重写)、static
(与类相关而非实例相关)或private
(无法被继承),则不能重写。
-
注解:
- 在Java中,可以使用
@Override
注解来标记子类中的重写方法。这是一种最佳实践,可以提高代码的可读性和可维护性。
- 在Java中,可以使用
-
动态绑定:
- 在运行时,Java会动态确定要调用的方法版本,这叫做动态绑定。
- 当使用父类引用变量引用子类对象并调用重写方法时,将调用子类的方法。
-
多态性:
- 方法重写是多态性的一种体现,它允许不同子类实现相同的方法名称,但可能具有不同的行为。
- 多态性使得编写通用代码更容易,因为方法可以处理多种不同类型的子类对象。
-
返回类型协变:
- 在方法重写中,子类重写的方法可以返回与父类方法相同或其子类型的值,这叫做"返回类型协变"。
- 在Java 5及更高版本中引入了这个特性,使得方法重写更加灵活。
方法重写和方法重载的区别
区别:
-
关于父类和子类:
- 方法重写是在子类中重新定义父类的方法,涉及继承关系。
- 方法重载在同一个类中定义多个方法,不涉及继承。
-
名称、返回类型和参数:
- 方法重写要求方法名称、返回类型和参数列表必须与父类方法相同。
- 方法重载要求方法名称相同,但参数列表不同。
-
动态绑定:
- 方法重写实现动态绑定,根据对象的实际类型调用正确的方法版本。
- 方法重载在编译时根据参数匹配来选择方法。
-
注解:
- 方法重写通常使用
@Override
注解。 - 方法重载不需要使用注解。
- 方法重写通常使用
示例:
class Animal {
void makeSound() {
System.out.println("Animal makes a sound.");
}
}
class Dog extends Animal {
@Override
void makeSound() {
System.out.println("Dog barks.");
}
void makeSound(String sound) {
System.out.println("Dog makes a custom sound: " + sound);
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Dog();
animal.makeSound(); // 调用重写的方法
Dog dog = new Dog();
dog.makeSound("Woof!"); // 调用重载的方法
}
}