Java基础复习
“任何时候我也不会满足,越是多读书,就越是深刻地感到不满足,越感到自己知识贫乏。科学是奥妙无穷的。” ——马克思
目录
一、方法&方法重载
二、运算符
三、数据类型
四、面向对象
1. 面向对象思想
2. 引用传递
3. 访问权限修饰符
4. 封装 、 构造方法 与 this
4-1 封装、构造方法
4-2 this
5. static 关键字 、 代码块
6. 继承、方法重写 、 super 与 final
6-1 继承
6-2 方法重写
6-3 super
6-4 final
7.抽象类 、 接口 、 多态
7-1 抽象类 - abstract
7-2 接口 interface
7-3 多态
7-4 instanceof - 对象是哪个类(接口)的实例
8. Object 类 、 “equal和==”
五、内部类
1. 成员内部类
2. 局部内部类
3. 静态内部类
4. 匿名内部类
六、异常
七、反射机制
1:实现动态创建对象
2:动态调用方法
3:访问和修改私有成员(字段、方法等
一、方法&方法重载
- 就是一段可以重复调用的代码
修饰符 返回值类型 方法名 (参数类型 参数名1,参数类型 参数名2,...){执行语句;return 返回值;
}
public static int add(int a,int b){int c = a + b;return c;
}
方法重载
- 定义:同一个作用域内,方法名相同但 参数个数 或者 参数类型 不同的方法
- 换句话说:参数不同的方法有相同的名字,调用时根据参数不同确定要调用哪个方法
public class Example01 {public static void main(String[] args) {int x = 1, y = 2;System.out.println(add(x, y));int z = 3;System.out.println(add(x, y, z));double a = 1.0, b = 2.0;System.out.println(add(a, b));}// 两个整数相加 public static int add(int x, int y) {return x + y;}// 三个整数相加public static int add(int x, int y, int z) {return x + y + z;}// 两个浮点数相加public static double add(double a, double b) {return a + b;}
}
二、运算符
- 赋值运算符
重点是 += 、 -=、/=、*=、%=
这些赋值运算符都不会改变原有的数据类型(但实际过程是先转为大的数据类型,然后再强转为原有数据类型)
- 运算符的优先级表格:
-
逻辑运算符中 异或(
^
):它的功能其实和!=
差不多,成立的话为true
,否则false
-
补充一个知识点:
当 id 依次递增 ( 1, 2 , 3 , 4 , 5 , 6 …) 时, 使用
(id+1)^1+1
可以得到相邻奇偶替换的结果原: 1, 2 , 3 , 4 , 5 , 6 …
每个数字经过
(id+1)^1+1
异或计算后得到 : 2, 1 , 4 , 3 , 6 , 5 …
-
-
与(
&
)、或(|
):不管条件是false还是true,两边条件都继续判断; -
短路与(
&&
):从左到右判断,左边出现false
就不再判断后续条件; -
短路或(
||
):从左到右判断,左边出现true
就不再判断后续条件;
三、数据类型
byte - 1字节
short char - 2字节
int float - 4字节
long double - 8字节
boolean - 至少1字节(JVM中占4字节,数组的话则为1字节)
四、面向对象
1. 面向对象思想
面向对象思想:将构成问题的事物按照一定的规则划分为多个独立的对象,通过调用对象来解决问题,其中对象包含属性和方法。
特点:封装、继承、多态
封装是指将数据和操作数据的方法封装在一个类中,对外提供特定的接口来访问这些数据和方法,而隐藏内部的实现细节。 -
private
继承是指在原有类(也就是父类)的属性和方法上进行继承以及进行功能扩展。 -
extend
多态是指不同子类继承父类后,属性和行为各有特点。
2. 引用传递
引用传递:
实际上所谓的引用传递就是,将一个堆内存空间的使用权分配给多个栈内存空间使用,每个栈内存空间都可以修改堆内存空间的内容。
注意:一个 栈
内存空间只能指向一个 堆
内存空间,如果想要再指向其他堆内存空间,就必须断开已有的指向,才能分配新的指向。
3. 访问权限修饰符
访问权限:(类中的属性和方法)
// 同包下的Test01类
public class Test01 {// 可以被所有类访问public int public_int;// 可以被所有子类及本包下的类访问protected boolean protected_boolean;// 默认访问权限,能在本包内访问void default_void(){System.out.println("在同一个包下,访问成功");}// 私有类,只能在本类访问private class private_class{ }
}
// 和Test01
public class Example03 {public static void main(String[] args) {Test01 test01 = new Test01();System.out.println("本包下访问情况:");// public : 所有类都可以访问System.out.println("test01.public_int = " + test01.public_int);test01.public_int = 1;System.out.println("test01.public_int = " + test01.public_int);// protected : 子类或者同包下System.out.println("test01.protected_boolean = " + test01.protected_boolean);test01.protected_boolean = true;System.out.println("test01.protected_boolean = " + test01.protected_boolean);// default : 同包下test01.default_void();// private : 不能访问,只能在类中访问// test01.private_class;}
}
public > protected > default > private
全局 > 子类 > 同包下 > 本类
注意:局部变量没有访问控制权限
4. 封装 、 构造方法 与 this
// 学生类
public class Student {private String name;private int age;/*** 构造方法注意事项:* ① 构造方法名字一定要和类名一致* ② 构造方法名称前面不能有 任何返回值 类型 (什么什么int呀,boolean呀,String呀等等...)* ③ 不能在构造方法里面用 `return` !! 要用的话也是 `return;` (也没什么意义);*/public Student() {System.out.println("使用了无参构造方法");return ;}/*** 有参构造* @param name* @param age*/public Student(String name,int age) {this.name = name;this.age = age;System.out.println("使用了有参构造方法");}/*** 构造方法重载,但是只能是构造方法 参数类型 或者 参数个数 不同* @param name*/public Student(String name){this.name = name;System.out.println("构造方法重载 - 参数个数不同");}public Student(float age){System.out.println("构造方法重载 - 参数类型不同");}public int getAge() {return age;}public String getName() {return name;}public void setAge(int age) {// 加一层限制,防止年龄异常// 为什么在这里加,是因为如果直接让用户访问对象的话,他哪管什么规则不规则,来了就乱设// 所以封装体现了面向对象的安全性if (age < 0){System.out.println("年龄不能小于0岁");} else if (age > 150){System.out.println("年龄不能超过150岁");} else {this.age = age;}}public void setName(String name) {this.name = name;}public void read() {System.out.println("大家好,我是" + name + ",今年我" + age + "岁,很高兴认识大家!");}}
// 实现类
public class Example04 {public static void main(String[] args) {// 实现封装特性:private 关键字,将对象的信息隐藏起来,只能使用对象的方法来操作对象的属性,防止恶意修改对象数据。Student student = new Student();// 这种什么什么setXxx呀,统称为setter方法student.setName("萌神想");student.setAge(22);// 调用对象的方法student.read();// 这种什么什么getXxx呀,统称为getter方法String name = student.getName();int age = student.getAge();System.out.println("大家好,我是" + name + ",今年我" + age + "岁,很高兴认识大家!");}
}
4-1 封装、构造方法
注意:构造方法、setter、getter、封装特性【private】、this关键字
构造方法注意事项:
① 构造方法名字一定要和类名一致
② 构造方法名称前面不能有 任何返回值 类型 (什么什么int呀,boolean呀,String呀等等…)
③ 不能在构造方法里面用 return
!! 要用的话也是 return;
(也没什么意义);
封装特性:
private
关键字,将对象的信息隐藏起来,只能使用对象的方法来操作对象的属性,防止恶意修改对象数据。
4-2 this
this 关键字:
① 本类的属性;
② 本类的成员方法;
③ 静态方法中不能有 this
;
④ 本类的构造方法 - 长这个样子: this()
注意1: this()
只能在构造方法里面使用,且它本身是一个无参构造;
注意2:在其它构造方法内,必须放在第一位;
注意3:不能在一个类中两个构造方法内使用 this
相互调用(否则会出现递归调用,会报错)
构造块:
构造块先于构造方法执行(与次序无关)
5. static 关键字 、 代码块
public class StudentSchool {private String name;private int age;private static String school = "东华理工大学";public StudentSchool() {System.out.println("使用了无参构造方法");}public StudentSchool(String name, int age, String school) {this.name = name;this.age = age;System.out.println("使用了有参构造方法");}public int getAge() {return age;}public String getName() {return name;}public static String getSchool() {return school;}public void setAge(int age) {// 加一层限制,防止年龄异常// 为什么在这里加,是因为如果直接让用户访问对象的话,他哪管什么规则不规则,来了就乱设// 所以封装体现了面向对象的安全性if (age < 0) {System.out.println("年龄不能小于0岁");} else if (age > 150) {System.out.println("年龄不能超过150岁");} else {this.age = age;}}public void setName(String name) {this.name = name;}public static void setSchool(String school) {StudentSchool.school = school;}public void read() {System.out.println("大家好,我是" + name + ",今年我" + age + "岁,我来自" + school + ",很高兴认识大家!");}}
public class Example05 {public static void main(String[] args) {// Static 静态方法StudentSchool studentSchool = new StudentSchool();studentSchool.setAge(22);studentSchool.setName("萌神想");studentSchool.read();// 要用类名去修改静态成员变量StudentSchool.setSchool("华中科技大学");studentSchool.read();}
}
注意:
① 静态属性也称为全局属性,可以使用类名直接访问;
② static不能修饰局部变量;
③ 静态方法只能访问静态成员(非静态成员需要先创建对象才能访问,即随对象的创建,非静态成员才会分配内存,而静态方法在被调用时可以不创建任何对象);
④ 静态代码块 → 构造代码块 → 构造方法;(静态代码块中的内容只输出一次,因为静态代码在类第一次使用时才会被加载,并且只被加载一次。)
public class StudentTest {private static String name;private static int n;// 静态构造块static {name = "执行顺序 ";n++;System.out.println(name + n +". 静态构造块");}// 主函数public static void main(String[] args){name = "执行顺序 ";n++;System.out.println(name + n + ". 主函数");StudentTest studentTest = new StudentTest();StudentTest studentTest1 = new StudentTest();}// 构造块{name = "执行顺序 ";n++;System.out.println(name + n + ". 构造块");}
}
执行顺序 1. 静态构造块
执行顺序 2. 主函数
执行顺序 3. 构造块
执行顺序 4. 构造块
6. 继承、方法重写 、 super 与 final
6-1 继承
继承:
类的继承是指在一个原有类的基础上构建一个新的类,构建的新类被称为子类,原有类称为父类;子类也可以定义自己的属性和方法。
子类会自动继承父类的属性和方法,关键字是 extend
;但是子类只能访问父类的 public、protected 修饰的属性或方法,不能访问 default、private 修饰的。
父类也被称为超类。
继承中的注意事项:
- 类只支持单继承,不允许多继承
class a{}
class b{}
class c extend a,b {}
// 这是错误的
- 多个类可以继承一个父类
class a{}
class b extend a {}
class c extend a {}
// 正确
- 多层继承也是可以的( 爷 → 父 → 子 → 孙)
6-2 方法重写
方法重写:
重写:子类对父类的方法进行重写。子类中重写父类的方法需要和父类中被重写的方法具有相同的方法名、参数列表、返回值类型。而且访问权限还不能升级(public 之后就一直是 public):**父类的方法被public修饰了,子类不能比public还严格!**这个意思。
6-3 super
super关键字:
使用super关键字可以在子类中访问父类的非私有方法、非私有属性以及构造方法。(除了private之外,其他default、protected、public都可以访问,这句话还是很严谨的)
通过super调用父类构造方法的代码必须位于子类构造方法的第一行,并且只能出现一次。
super 与 this 的区别:
区别点 | super | this |
---|---|---|
访问属性 | 直接访问父类非私有属性 | 访问本类中的属性。如果本类没有则在父类上找 |
调用方法 | 直接访问父类非私有方法 | 调用本类中的方法。如果没有则在父类上找 |
调用构造方法 | 调用父类构造方法,必须放在子类构造方法的首行 | 调用本类构造方法,必须放在构造方法的首行 |
注意:super与this不能同时出现在同一个构造方法内
6-4 final
final关键字:可以修饰的有:类、属性、方法
final注意:
-
使用final关键字修饰的类不能有子类,也就是不能被继承
-
使用final关键字修饰的方法不能被重写,也就是子类不能重写父类中被final修饰的方法
-
使用final关键字修饰的属性是常量,常量不可被修改
-
和static一起用的时候,如下
// 全局常量
public static final MAX_LENGTH = 100;
7.抽象类 、 接口 、 多态
7-1 抽象类 - abstract
抽象类产生原因:定义一个类时,常常需要定义一些成员方法描述类的行为特征,但有时这些方法的实现方法是不确定的,所以抽象类必然要满足这个要求。
抽象类包含:成员变量、抽象方法。(然后没了)
抽象方法是使用 abstract
关键字修饰的成员方法,抽象方法在定义时,不需要实现方法体。
抽象方法定义规则:
- 包含抽象方法的类必然是抽象类;
- 声明抽象类和抽象方法时都需要使用
abstract
关键字修饰; - 抽象方法只需要声明而不需要实现;
- 如果一个非抽象类继承了抽象类后,那么该类必须重写抽象类中的全部抽象方法;
// 定义抽象动物类
abstract class Animal08{// 定义抽象类成员变量public String name;// 定义抽象方法abstract void shout();
}
// 定义小狗类
class Dog extends Animal08{// 重写抽象方法@Overridepublic void shout(){System.out.println("汪汪汪...");}
}
public class Example08 {public static void main(String[] args) {Dog dog = new Dog();dog.shout();}
}
注意:不能使用 private
修饰 abstract ,因为抽象方法你要重写才能有用,如果把方法私有,那子类就没有权限重写方法,也就没意义了。
7-2 接口 interface
接口很重要,你不懂不行。
接口是一种用来定义程序的协议,它用来描述类或结构的一组相关行为。
面向接口编程:将程序不同的业务逻辑分离,以接口的形式对接不同的业务模块。
使用接口的目的是为了克服单继承的限制,因为一个类只能继承一个父类,而一个类可以同时实现多个父接口。
在 JDK8 之前,接口都是由全局常量和抽象方法组成。
在 JDK8 之后,接口除了全局常量、抽象方法外,还可以定义默认方法和静态方法,默认方法用default修饰,静态用static修饰,而且这两种方法都允许有方法体。
[public] interface 接口名 [extend 接口1,extend 接口2 ,...]{// 这两个是 jdk8 之前的[public] [static] [final] 数据类型 常量名 = 常量;[public] [abstract] 返回值的数据类型 方法名(参数列表);// 接下来两个是 jdk8 新加的[public] static 返回值的数据类型 方法名(参数列表) {}[public] default 返回值的数据类型 方法名(参数列表) {}
}
extend 接口1,extend 接口2 ,...
表示 一个接口可以继承多个父接口;- 接口中变量默认使用
public static final
进行修饰,即全局变量,且一定要赋初始值; - 接口中定义的抽象方法默认使用
public abstract
修饰;
接口本身不能直接实例化,接口中的抽象方法和默认方法只能通过接口实现类的实例对象进行调用
实现类通过 implements
关键字实现接口,并且实现类必须重写接口中所有的抽象方法
接口不允许继承抽象类(interface 接口名 extend 抽象方法 ×,抽象方法 implements 接口 √)但是允许接口继承接口,并且一个接口可以同时继承多个接口
// 定义接口动物
interface Animal09{// 定义全局常量String NAME = "牧羊犬";String SEX = "男";// 默认 公共抽象方法void shout();void info();// jdk8 中的静态方法static String getNAME(){return Animal09.NAME;};// jdk8 中的默认方法default void getSEX(){System.out.println(Animal09.SEX);}
}
// 定义抽象类 Action
abstract class Action09{// 抽象方法public abstract void eat();
}
// 定义小狗类
class Dog09 extends Action09 implements Animal09{// 重写抽象方法 eat()@Overridepublic void eat(){System.out.println("喜欢吃骨头");}// 重写接口抽象方法 shout()@Overridepublic void shout(){System.out.println("汪汪汪...");}@Overridepublic void info(){System.out.println(Animal09.getNAME());}
}public class Example09 {public static void main(String[] args) {Dog09 dog = new Dog09();// 接口 公共抽象方法 实现dog.info();// 接口 默认方法 实现(子类可以不用重写这个方法,因为是default修饰的)dog.getSEX();// 接口 公共抽象方法 实现dog.shout();// 抽象类 公共抽象方法 实现dog.eat();}
}
7-3 多态
多态的概念很重要,也很抽象,希望你一定要掌握!
多态指不同类的对象在调用同一个方法时表现出的多种不同行为。换句话说:在同一个方法中,这种由于参数类型不同而导致执行效果不同的现象就是多态。
主要有以下两种形式:
- 方法的重载
- 对象的多态(方法的重写)
// 定义抽象类 Animal10
abstract class Animal10 {// 抽象方法public abstract void shout();
}// 定义小狗类 Dog10
class Dog10 extends Animal10{@Overridepublic void shout(){System.out.println("汪汪汪...");}
}// 定义小猫类 Cat10
class Cat10 extends Animal10{@Overridepublic void shout(){System.out.println("喵喵喵...");}
}public class Example10 {public static void main(String[] args) {System.out.println("输出的结果:");Animal10 dog = new Dog10();Animal10 cat = new Cat10();dog.shout();cat.shout();}
}// 输出的结果:
// 汪汪汪...
// 喵喵喵...
为了让你一定明白,我这么说吧,先把定义背下来:多态指不同类的对象在调用同一个方法时表现出的多种不同行为。然后思考,不同类(上面代码中的 dog10
类 和 cat10
类 )在main方法中创建了对象,但是声明的类型是父类( Animal10
类),在调用同一个方法( shout()
)的时候,表现出了不同的结果(汪汪汪...
和 喵喵喵...
),这就是多态。父类的引用指向自己子类的对象时多态的一种体现形式。
对象类型的转化:
- 向上转型:子类对象 → 父类对象
- 向下转型:父类对象 → 子类对象
向上转型:父类对象可以调用子类重写父类的方法,这样当添加新功能的时候,只需要新增一个子类,在子类中对父类的功能进行扩展,而不用更改父类的代码,保证了程序的安全性。
格式: 父类类型 父类对象 = 子类实例 ;
特别注意:父类对象不能用子类特有的方法,因为父类没有定义那个方法。
向下转型:一般是为了重新获得因为向上转型而失去的子类特性。对象在进行向下转型前,需要先进行向上转型,才能进行向下转型。
格式: 父类类型 父类对象 = 子类实例 ; 子类类型 子类对象 = (子类)父类对象 ;
特别注意:尽管你是向下转型,但是只要子类改了你父类的方法,那你父类再调用那个方法的时候,已经是子类修改后的方法了(你调用不了你之前的方法)。
多态的作用: 使得面向对象编程更加灵活、可扩展和易于维护。
- 代码可扩展性:通过多态,可以在不修改现有代码的情况下添加新的类和功能。只需要实现相同的接口或继承自同一基类即可。
- 灵活性:多态允许同一方法根据对象的具体类型表现出不同的行为。这使得程序在运行时可以决定使用哪种具体实现。
- 可维护性:多态性降低了程序的复杂性,使得代码更易于理解和维护。程序中的变化可以集中在某些地方,而不需改动整个系统。
- 提高代码重用率:通过定义通用的接口,开发者可以编写更具复用性的代码,减少重复实现。
- 实现抽象:多态与抽象密切相关,允许程序员基于接口或抽象类来编写代码,而不是关注具体的实现细节。
- 简化条件语句:通过多态,在处理不同类型的对象时,可以避免使用大量的条件语句,从而使代码更加简洁和清晰。
7-4 instanceof - 对象是哪个类(接口)的实例
instanceof 关键字:判断一个对象是否是某个类(或接口)的实例
// instanceof 格式 - 相同则为true,否则为false
实例后的对象 instanceof 待验证的类(或接口);
8. Object 类 、 “equal和==”
Object类是所有类的父类,每个类都直接或间接继承了Object类,因此这个类通常被称为超类(基类)。当定义一个类时,如果没有使用extends关键字为这个类显式地指定父类,那么该类会被默认继承Object类。
方法名称 | 方法说明 |
---|---|
boolean equal() | 判断两个对象是否“相等” |
int hashCode() | 返回对象的哈希值 |
String toString() | 返回对象的字符串表示形式 |
通常会重写 toString()
并将它用来表示此类的基本信息。
扩展:在 Java 中,“==” 和 “equals ()” 方法都可以用于比较,但它们的比较方式有所不同。
一、“==” 运算符
- 基本数据类型比较:
- 当用于比较基本数据类型(如 int、double、char 等)时,“==” 比较的是两个值是否相等。例如,
int a = 5; int b = 5;
,此时a == b
会返回 true,因为两个变量存储的值都是 5。- 引用数据类型比较:
- 当用于比较引用数据类型(如对象)时,“==” 比较的是两个引用是否指向同一个对象。例如:
String s1 = new String("hello"); String s2 = new String("hello"); System.out.println(s1 == s2); // 输出 false
在这个例子中,虽然
s1
和s2
的内容都是 “hello”,但它们是两个不同的对象,所以 “==” 比较返回 false。二、“equals ()” 方法
- 对于自带的类,如
String
、Integer
等包装类,equals()
方法被重写,比较的是对象的内容是否相等。例如:String s1 = new String("hello"); String s2 = new String("hello"); System.out.println(s1.equals(s2)); // 输出 true
这里
s1
和s2
虽然是不同的对象,但它们的内容相同,所以 “equals ()” 比较返回 true。
- 对于自定义类,如果没有重写
equals()
方法,那么默认的行为与 “==” 相同,也是比较引用是否指向同一个对象。如果需要比较自定义类对象的内容是否相等,就需要重写equals()
方法。例如:class Person {private String name;private int age;Person(String name, int age) {this.name = name;this.age = age;}@Overridepublic boolean equals(Object o) {// 如果当前对象(this)和传入的对象(o)是同一个对象引用,那么直接返回trueif (this == o) return true;// 传入的对象(o)是空或者类型与当前对象类型不一样,则返回falseif (o == null || getClass()!= o.getClass()) return false;Person person = (Person) o;return age == person.age && Objects.equals(name, person.name);}@Overridepublic int hashCode() {return Objects.hash(name, age);} }
在这个例子中,重写了
Person
类的equals()
方法,使得可以比较两个Person
对象的内容是否相等。
五、内部类
1. 成员内部类
成员内部类可以访问外部类的所有成员,无论外部类的成员是何种访问权限。如果想外部类访问内部类,则需要通过创建内部类对象才能进行访问。
// 内部类
public class Example11 {public static void main(String[] args) {Outer outer = new Outer();Outer.Inner inner = outer.new Inner();inner.show1();outer.test2();}}class Outer {int m = 0;// 外部类方法 test1()void test1() {System.out.println("外部类成员方法test1()");}// 内部类Innerclass Inner {int n = 1;void show1() {// 访问外部类的成员变量 mSystem.out.println("外部类的成员变量 m = " + m);// 访问外部类的方法 test1()test1();}void show2() {// 访问内部类的成员变量nSystem.out.println("内部类的成员方法show2()");}}// 外部类方法 test2()void test2() {Inner inner = new Inner();System.out.println("内部类成员变量 n = " + inner.n);inner.show2();}
}
注意:内部类的定义方式
2. 局部内部类
局部内部类,也称为方法内部类,是指定义在某个局限范围中的类,它和局部变量都是在方法中定义,有效范围只限于方法内部。
局部内部类权限和成员内部类一样,都可以直接访问外部类的所有成员和成员方法,无论权限。如果外部类想访问内部类,只能在局部内部类所在的方法中去创建对象并访问。
// 内部类
public class Example12 {public static void main(String[] args) {Outer1 outer = new Outer1();outer.test2();}}class Outer1 {int m = 0;// 外部类方法 test1()void test1() {System.out.println("外部类成员方法test1()");}// 外部类方法 test2()void test2() {// 局部内部类Innerclass Inner {int n = 1;void show() {// 访问外部类的成员变量 mSystem.out.println("外部类的成员变量 m = " + m);// 访问外部类的方法 test1()test1();}}Inner inner = new Inner();System.out.println("内部类成员变量 n = " + inner.n);inner.show();}Inner inner = new Inner();
}
注意:局部内部类
只能在 局部内部类所在的方法
里面创建 局部内部类对象
,否则报错。
3. 静态内部类
静态内部类,使用static关键字修饰的成员内部类。与成员内部类相比,形式上只是加了个static,但是功能上有点阉割了,因为只能访问外部类的静态成员。外部类访问静态内部类时,因为程序已经提前在静态常量区为静态内部类分配好了内存,所以直接创建一个静态内部类没问题。
// 内部类
public class Example13 {public static void main(String[] args) {Outer2.Inner inner = new Outer2.Inner();inner.show1();System.out.println("静态内部类的成员变量 n = " + inner.n);}}class Outer2 {static int m = 0;// 内部类Innerstatic class Inner {int n = 1;void show1() {// 访问外部类的成员变量 mSystem.out.println("外部类的成员变量 m = " + m);}}
}
注意:静态内部类的定义方式
4. 匿名内部类
调用某个方法时,如果该方法的参数时接口类型,那么在传参时,除了可以传入一个接口实现了类,还可以传入一个实现接口的匿名内部类作为参数,在匿名内部类实现(重写)接口的方法。
// 内部类
public class Example15 {public static void main(String[] args) {animalShout(new Animal15() {@Overridepublic void shout() {System.out.println("匿名内部类来重写你");}});}public static void animalShout(Animal15 animal15){animal15.shout();}
}interface Animal15 {void shout();
}
也可以替换成 Lambda 表达式
// 内部类
public class Example15 {public static void main(String[] args) {animalShout(() -> System.out.println("匿名内部类来重写你"));}public static void animalShout(Animal15 animal15){animal15.shout();}
}interface Animal15 {void shout();
}
Lambda 表达式扩展( JDK8 之后引入的):
在 Java 中,Lambda 表达式是一种简洁的方式来表示可传递给方法或存储在变量中的匿名函数。
一、作为函数式接口的实现
假设你有一个需求,要对一组数字进行操作,比如将每个数字乘以 2 后再打印出来。可以使用
Consumer<Integer>
函数式接口和 Lambda 表达式来实现这个功能。import java.util.Arrays; import java.util.List; import java.util.function.Consumer;public class LambdaExample {public static void main(String[] args) {List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);// 使用 Lambda 表达式实现 Consumer 接口,打印每个数字乘以 2 的结果Consumer<Integer> doubleAndPrint = num -> System.out.println(num * 2);numbers.forEach(doubleAndPrint);} }
在这个例子中:
Consumer<Integer>
表示一个接受整数参数但不返回结果的操作。doubleAndPrint
是一个使用 Lambda 表达式创建的Consumer
接口的实现,它接受一个整数参数num
,将其乘以 2 后打印出来。numbers.forEach(doubleAndPrint)
遍历列表中的每个数字,并将每个数字传递给doubleAndPrint
这个操作进行处理。二、在集合操作中的使用
假设你有一个班级学生名单,你想找出名字长度大于 4 的学生并打印他们的名字。
import java.util.ArrayList; import java.util.List;public class LambdaInCollectionExample {public static void main(String[] args) {List<String> students = new ArrayList<>();students.add("Tom");students.add("Jerry");students.add("Alice");students.add("Bob");students.add("Eve");// 使用 Lambda 表达式和流操作筛选并打印名字长度大于 4 的学生students.stream().filter(name -> name.length() > 4).forEach(System.out::println);} }
在这个例子中:
- 首先创建了一个包含学生名字的列表
students
。- 然后使用
stream()
方法将列表转换为流。filter(name -> name.length() > 4)
使用 Lambda 表达式作为筛选条件,只保留名字长度大于 4 的元素。forEach(System.out::println)
使用方法引用作为 Lambda 表达式的简化形式,打印每个满足条件的学生名字。三、作为方法参数传递
假设你正在开发一个游戏,游戏中有不同类型的角色,每个角色都有不同的攻击方式。你可以定义一个方法来执行角色的攻击动作,而这个攻击动作可以通过 Lambda 表达式作为参数传递进去。
public class LambdaAsMethodParameterExample {public static void main(String[] args) {Warrior warrior = new Warrior();Mage mage = new Mage();// 使用方法引用作为 Lambda 表达式传递给 performAttack 方法performAttack(warrior::attackWithSword);performAttack(mage::attackWithMagic);}static void performAttack(Runnable attackAction) {System.out.println("Preparing to attack...");// 执行传入的 Runnable(即 Lambda 表达式或方法引用代表的行为)attackAction.run();System.out.println("Attack completed.");} }class Warrior {void attackWithSword() {System.out.println("Warrior attacks with sword.");} }class Mage {void attackWithMagic() {System.out.println("Mage attacks with magic.");} }
在这个例子中:
- 定义了一个方法
performAttack
,它接受一个Runnable
类型的参数attackAction
,这个参数代表一个可以执行的攻击动作。- 在
main
方法中,分别为战士(Warrior
)和法师(Mage
)创建实例,并将他们的攻击方法(分别是attackWithSword
和attackWithMagic
)作为 Lambda 表达式传递给performAttack
方法。- 当
performAttack
方法被调用时,它会先打印“Preparing to attack…”,然后执行传入的攻击动作,最后打印“Attack completed.”。通过这种方式,使用 Lambda 表达式作为方法参数,可以使代码更加灵活,能够适应不同的行为需求,而无需为每个具体的行为编写单独的方法。
四、在并行流中的使用
假设你有一个任务是统计一个包含很多整数的列表中所有偶数的平方和。如果使用传统的方法,可能需要遍历整个列表,找到偶数,计算平方,然后累加求和,这可能会比较耗时。而使用并行流和 Lambda 表达式可以充分利用多核处理器的优势,加快计算速度。
import java.util.ArrayList; import java.util.List; import java.util.stream.IntStream;public class ParallelStreamExample {public static void main(String[] args) {List<Integer> numbers = new ArrayList<>();for (int i = 1; i <= 10000; i++) {numbers.add(i);}int sumOfEvenSquares = numbers.parallelStream().filter(n -> n % 2 == 0).map(n -> n * n).sum();System.out.println("Sum of squares of even numbers: " + sumOfEvenSquares);} }
在这个例子中:
- 首先创建了一个包含 1 到 10000 的整数列表
numbers
。- 然后使用
parallelStream()
方法将列表转换为并行流。filter(n -> n % 2 == 0)
使用 Lambda 表达式筛选出偶数。map(n -> n * n)
使用 Lambda 表达式将每个偶数转换为其平方。- 最后,
sum()
方法计算所有偶数平方的总和。通过使用并行流和 Lambda 表达式,Java 可以自动将这个任务分配到多个处理器核心上同时执行,从而大大提高计算效率。特别是当处理大量数据时,这种优势会更加明显。
六、异常
所有异常都继承自 Throwable
类 ,主要分为 Error类(程序中产生的错误) 、 Exception类(程序中产生的异常)
-
Error类 : 比较严重,表示java程序运行时产生的系统内部错误或资源耗尽的错误。仅修改程序不能解决问题;
-
Exception类 : 重点是 运行时异常( RuntimeException ) ,修改程序本身可以解决问题
Throwable类的常用方法
方法声明 | 功能描述 |
---|---|
String getMessage() | 返回异常的消息字符串 |
String toString() | 返回异常的简单信息描述 |
void printStackTrace() | 获取异常类名和异常信息以及异常出现在程序中的位置,并把信息输出 |
异常处理五个关键字:try、catch、finally、throws、throw
重点是 : 自定义异常
public class Example19 {public static void main(String[] args) {try{divide(3,0);} catch (DivideByMinusException e){System.out.println("捕获异常:" + e.getMessage());}}public static void divide(int x,int y) throws DivideByMinusException{if (y == 0) {throw new DivideByMinusException("除数不能等于0");}System.out.println(x/y);}
}class DivideByMinusException extends Exception{public DivideByMinusException(){super();}public DivideByMinusException(String message){super(message);}
}
System 类中 返回 当前以毫秒为单位的当前时间 static void currentTimeMillis();
七、反射机制
反射机制在Java中有以下几个重要作用,下面为你举例说明:
1:实现动态创建对象
- 作用:在编写代码时,有时候我们没办法提前确定要创建哪个类的对象,反射就可以让程序在运行时根据条件动态地去创建不同类的实例。
- 举例:假设你在开发一个游戏,游戏里有不同类型的角色,比如“战士”“法师”“刺客”等,每个角色对应一个类,它们都继承自一个公共的“角色”基类。游戏开始前,玩家可以选择要使用的角色类型,而你在代码里事先并不知道玩家会选哪种角色。这时候就可以利用反射,根据玩家的选择来动态创建对应的角色对象。比如下面这样简单的代码示意:
import java.lang.reflect.Constructor;class Warrior {public Warrior() {System.out.println("创建了战士角色");}
}class Mage {public Mage() {System.out.println("创建了法师角色");}
}class Game {public static void createRole(String roleClassName) throws Exception {Class<?> roleClass = Class.forName(roleClassName);Constructor<?> constructor = roleClass.getConstructor();constructor.newInstance();}
}public class ReflectionRoleExample {public static void main(String[] args) throws Exception {// 假设玩家选择了"Warrior"这个角色类型String selectedRole = "Warrior";Game.createRole("com.example." + selectedRole);}
}
这里根据玩家选择的角色类型字符串(假设类都在com.example
包下),通过反射找到对应的类并创建实例,如果玩家选的是“法师”,把selectedRole
改成"Mage"
就能创建出法师角色的对象了。
2:动态调用方法
- 作用:能在运行时根据具体情况调用不同类中的不同方法,不需要在编译阶段就把调用的方法固定下来。
- 举例:有一个图形绘制的程序,里面有圆形、矩形等不同图形类,每个类都有自己的
draw
方法来进行图形绘制。程序中有个功能是根据用户输入来绘制相应图形,通过反射就可以动态调用对应的draw
方法。示例代码如下:
import java.lang.reflect.Method;class Circle {public void draw() {System.out.println("正在绘制圆形");}
}class Rectangle {public void draw() {System.out.println("正在绘制矩形");}
}class Drawer {public static void drawShape(String shapeClassName) throws Exception {Class<?> shapeClass = Class.forName(shapeClassName);Object shapeObj = shapeClass.getConstructor().newInstance();Method drawMethod = shapeClass.getMethod("draw");drawMethod.invoke(shapeObj);}
}public class ReflectionDrawExample {public static void main(String[] args) throws Exception {// 假设用户选择绘制圆形String selectedShape = "Circle";Drawer.drawShape("com.example." + selectedShape);}
}
根据用户选择的图形类名字符串(同样假设类都在com.example
包下),利用反射找到对应的类、创建实例并调用其draw
方法,实现动态绘图的操作。
3:访问和修改私有成员(字段、方法等)
- 作用:正常情况下,类的私有成员是不能在外部类直接访问和修改的,但反射可以突破这个限制,在一些特定场景下,比如做单元测试、框架底层实现等时很有用。
- 举例:假设有个
Person
类,里面有个私有字段private String password
,在做单元测试时,想要验证修改密码的方法是否正确,就可以通过反射来访问和修改这个私有字段进行测试。示例代码如下:
import java.lang.reflect.Field;class Person {private String password;public Person(String password) {this.password = password;}public String getPassword() {return password;}
}class TestPerson {public static void main(String[] args) throws Exception {Person person = new Person("123456");Field passwordField = Person.class.getDeclaredField("password");passwordField.setAccessible(true);passwordField.set(person, "654321");System.out.println("修改后的密码: " + person.getPassword());}
}
这里通过反射获取到Person
类的私有字段password
,设置其可访问后就能修改它的值了,便于进行相关的测试等操作。
相关文章:

Java基础复习
“任何时候我也不会满足,越是多读书,就越是深刻地感到不满足,越感到自己知识贫乏。科学是奥妙无穷的。” ——马克思 目录 一、方法&方法重载 二、运算符 三、数据类型 四、面向对象 1. 面向对象思想 2. 引用传递 3. 访问权限修饰…...

简易图书管理系统
javawebjspservlet 实体类 package com.ghx.entity;/*** author :guo* date :Created in 2024/12/6 10:13* description:* modified By:* version:*/ public class Book {private int id;private String name;private double pri…...

结构型-组合模式(Composite Pattern)
什么是组合模式 又名部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。 结构 抽象根节点(Co…...

【知识堂】大数据
一、大数据的基本概念 什么是大数据? 大数据(Big Data)是指无法通过传统工具和方法在合理时间内处理的海量数据集合。其关键特征是4V,即数据量大(Volume)、数据种类多(Variety)、处…...

力扣C语言刷题记录(三)搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 示例 1: 输入: nums [1,3,5,6], target 5 输出: 2示例 2: 输入:…...

在Node.js局域网调试https的Vue项目
需求: 最近在测试在网页端(HTML5)调用移动设备的定位等权限功能,发现某些功能是必须保证域名在https下的否则会出现不正常现象。 解决: 1.在线生成和证书 访问:CSR文件生成工具-中国数字证书CHINASSL …...

3.5 认识决策树
3.5 认识决策树 3.5.1 认识决策树 如何高效的进行决策? 特征的先后顺序 3.5.2 决策树分类原理详解 已知有四个特征,预测 是否贷款给某个人。 先看房子,再看工作,是否贷款。 年龄,信贷情况,工作&#…...

股市复盘笔记
复盘是股市投资中非常重要的一个环节,它指的是投资者在股市收盘后,对当天的市场走势、个股表现以及自己的交易行为进行回顾和总结,以便更好地指导未来的投资决策。以下是对复盘的详细解释: 一、复盘的目的 总结市场走势ÿ…...

Canal 深入解析:从原理到实践的全面解读
Canal 深入解析:从原理到实践的全面解读 官网:https://github.com/alibaba/canal Canal 是阿里巴巴开源的一款分布式增量数据同步工具,广泛应用于数据同步、实时数据处理和数据库的增量备份等场景。它可以通过监听 MySQL 数据库的 binlog&am…...

SQL SERVER 2016 AlwaysOn 无域集群+负载均衡搭建与简测
之前和很多群友聊天发现对2016的无域和负载均衡满心期待,毕竟可以简单搭建而且可以不适用第三方负载均衡器,SQL自己可以负载了。windows2016已经可以下载使用了,那么这回终于可以揭开令人憧憬向往的AlwaysOn2016 负载均衡集群的神秘面纱了。 …...

解决 Maven 部署中的 Artifact 覆盖问题:实战经验分享20241204
🛠️ 解决 Maven 部署中的 Artifact 覆盖问题:实战经验分享 📌 引言 在软件开发过程中,持续集成和持续部署(CI/CD)是提高开发效率和代码质量的关键手段。Hudson 和 Maven 是两种广泛使用的工具࿰…...

【开源免费】基于SpringBoot+Vue.JS中小型医院网站(JAVA毕业设计)
博主说明:本文项目编号 T 078 ,文末自助获取源码 \color{red}{T078,文末自助获取源码} T078,文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析…...

Linux CentOS
阿里云开源镜像下载链接 https://mirrors.aliyun.com/centos/7/isos/x86_64/ VMware 安装 CentOS7 自定义 下一步 选择稍后安装操作系统 选择 输入 查看物理机CPU内核数量 CtrlShiftEsc 总数不超过物理机内核数量 推荐内存 自选 推荐 推荐 默认 拆分成多个 默认 自定义硬件…...

Android SurfaceFlinger layer层级
壁纸作为显示的最底层窗口它是怎么显示的 1. SurfaceFlinger layer层级 锁屏状态dump SurfaceFlinger ,adb shell dumpsys SurfaceFlinger Display 0 (active) HWC layers: -----------------------------------------------------------------------------------…...

spark-sql配置教程
1.前期准备 (1)首先要把hadoop集群,hive和spark等配置好 hadoop集群,hive的配置可以看看这个博主写的博客 大数据_蓝净云的博客-CSDN博客 或者看看黑马程序员的视频 黑马程序员大数据入门到实战教程,大数据开发必…...

生成表格pdf格式
1. 添加依赖 <dependency><groupId>com.itextpdf</groupId><artifactId>kernel</artifactId><version>7.2.5</version></dependency><dependency><groupId>com.itextpdf</groupId><artifactId>layout…...

C++ 游戏开发的前沿趋势:从光线追踪到人工智能的全新挑战
随着游戏行业的快速发展,技术的不断进步为游戏开发带来了前所未有的机遇和挑战。从逼真的光影效果到复杂的物理模拟,再到智能化的非玩家角色(NPC)行为和玩家交互,现代游戏的技术需求已经超越了传统的图形渲染与场景搭建…...

微信小程序3-显标记信息和弹框
感谢阅读,初学小白,有错指正。 一、实现功能: 在地图上添加标记点后,标记点是可以携带以下基础信息的,如标题、id、经纬度等。但是对于开发来说,这些信息还不足够,而且还要做到点击标记点时&a…...

EasyNVR中HTTP-FLV协议无法播放怎么解决?
在科技日新月异的今天,摄像头作为公共安全领域的重要一环,其技术的不断提升正显著地改变着社会的安全格局。从最初的简单监控到如今的高清智能分析,我们可以对特定区域进行实时监控和记录,为社会的安全稳定提供了强有力的保障。 问…...

spring cloud之ribbon复习回顾
其实在项目中直接使用ribbon时不多,大多是使用feign的,其实feign底层也是通过ribbon构建的,主要记忆一下计算规则,ribbon的源码还是很不错的,还是值得学习的。 1、添加pom <dependency><groupId>org.spr…...

RFT 强化微调
OpenAI在今天发布的新技术,RFT结合了SFT和RL的优化算法,与传统的监督微调不同,强化微调旨在通过任务训练让模型掌握复杂推理能力,而不仅仅是“记住答案”。 什么是强化微调 强化微调是通过高质量任务数据和参考答案优化大语言模型…...

SpringBoot教程(三十二) SpringBoot集成Skywalking链路跟踪
SpringBoot教程(三十二) | SpringBoot集成Skywalking链路跟踪 一、Skywalking是什么?二、Skywalking与JDK版本的对应关系三、Skywalking下载四、Skywalking 数据存储五、Skywalking 的启动六、部署探针 前提: Agents 8.9.0 放入 …...

分布式搜索引擎Elasticsearch
Elasticsearch是一个基于Lucene库的开源分布式搜索引擎,它被设计用于云计算中,能够实现快速、near-real-time的搜索,并且可以进行大规模的分布式索引。 以下是一个简单的Python代码示例,展示如何使用Elasticsearch的Python客户端…...

在Vue.js中生成二维码(将指定的url+参数 生成二维码)
在Vue.js中生成二维码,你可以使用JavaScript库如qrcode或qr.js。以下是一个简单的例子,展示如何在Vue组件中使用qrcode库将指定的URL加上参数生成二维码。 首先,你需要安装qrcode库。如果你使用的是npm或yarn,可以通过命令行安装…...

统信桌面专业版部署postgresql-14.2+postgis-3.2方法介绍
文章来源:统信桌面专业版部署postgresql-14.2postgis-3.2方法介绍 | 统信软件-知识分享平台 应用场景 CPU架构:X86(海光C86-3G 3350) OS版本信息:1070桌面专业版 软件信息:postgresql-14.2postgis-3.2 …...

数字图像处理(16):RGB与HSV互转
(1)HSV颜色模型:HSV颜色模型,又称为六角锥体模型,以色调(H)、饱和度(S)、亮度(V)为基础,能够更加自然地表现和处理颜色,因…...

web组态可视化编辑器
随着工业智能制造的发展,工业企业对设备可视化、远程运维的需求日趋强烈,传统的单机版组态软件已经不能满足越来越复杂的控制需求,那么实现web组态可视化界面成为了主要的技术路径。 行业痛点 对于软件服务商来说,将单机版软件转…...

数组 - 八皇后 - 困难
************* C topic: 面试题 08.12. 八皇后 - 力扣(LeetCode) ************* Good morning, gays, Fridary angin and try the hard to celebrate. Inspect the topic: This topic I can understand it in a second. And I do rethink a movie, …...

【分布式】Redis分布式缓存
一、什么是Redis分布式缓存 Redis分布式缓存是指使用Redis作为缓存系统来存储和管理数据的分布式方案。在分布式系统中,多台服务器共同对外提供服务,为了提高系统的性能和可扩展性,通常会引入缓存来减轻数据库的压力。Redis作为一种高性能的…...

Ubuntu——extrepo添加部分外部软件源
extrepo 是一个用于 Ubuntu 和其他基于 Debian 的系统的工具,它的主要作用是简化和管理外部软件源(repositories)的添加和更新。通过使用 extrepo,用户可以方便地添加、删除和管理第三方软件源,而不需要手动编辑源列表…...