类和对象
对象、面向对象以及面向对象编程概念
1.对象——行动或思考的目标,万物皆对象
2.面向对象——以属性和行为的观点去分析现实生活中的事物——把构成问题的事务分解成各个对象
3.面向过程——分析解决问题所需要的步骤,然后一步一步实现
4.面向对象编程(SE核心)——以面向对象的思想进行分析,然后使用面向对象的编程语言进行表达的过程
类和对象概念
1.对象——指现实生活中客观存在的实体,在Java中对象体现为内存空间中的一块存储区域
2.类——对具有相同特征和行为的对个对象共性的抽象描述,在Java中体现为引用数据类型,包含了描述特征/属性的成员变量以及描述成员变量的成员方法
3.类是构建对象的模板,对象的数据结构有定义它的类决定
类和对象的定义
class 类名{
类体;
}
class Person{
}
注意:
当类名有多个单词时,每个词首字母都要大写
成员变量的定义
class 类名{
数据类型 成员变量名 = 初始值;
}
class Person{
String name; //成员变量
}
注意:
当成员变量有多个单词组成时,通常要求从第二个单词起每个词首字母大写
对象的创建
new 类名();
new Person();
注意:
1.当一个类定义完后,可以使用new关键字来创建该类的对象,该过程叫做类的实例化
2.创建对象的本质就是在内存空间中申请一块存储区域,用于存放该该对象独有特征信息
引用的定义
1.使用引用数据类型定义额变量叫做引用变量,简称”引用“
2.引用变量主要用于记录对象在堆中的内存地址信息,便于下次访问
语法格式:
类名 引用变量名;
引用变量名.成员变量名;
Person p = new Person();
p.name = "张飞";
System.out.println(p.name);
Person类定义
成员变量初始值
成员变量类型 | 默认初始值 |
数值类型byte、short、int、long、float、double、char | 0 |
boolean型 | false |
引用类型 | null |
/**
* @auther weiwei
* @date 2021/3/24 16:10
* @description 编程实现Person类的定义
*/
public class Person {
// 数据类型 成员变量名 = 初始值; ————其中 =初始值 通常省略不写
String name; // 描述姓名的成员变量
int age; // 描述年龄的成员变量
public static void main(String[] args) {
// 1.声明Person类型的引用指向Person类型的对象
// 数据类型(类名) 引用变量名 = new 类名();
Person p = new Person();
// 2.打印对象中的成员变量值
// 应用变量名.成员变量名
System.out.println("我是" + p.name + ", " + "今年" + p.age + "岁了!"); // null 0
System.out.println("------------------------");
// 3.修改成员变量的数值
p.name = "张飞";
p.age = 30;
// 4.再次打印修改后的数值
System.out.println("我是" + p.name + ", " + "今年" + p.age + "岁了!"); // null 0
}
}
代码执行流程和内存分析
方法区:存放类
栈区:存放main方法
堆区:存放 new Person();对象
Point类的定义
/**
* @auther weiwei
* @date 2021/3/24 16:31
* @description 编程实现Point类的定义
*/
public class Point {
int x; // 横坐标
int y; // 纵坐标
public static void main(String[] args) {
// 1.声明Point类型引用指向Point类型对象
Point p = new Point();
// 打印成员变量数值
System.out.println("横坐标是: " + p.x + "纵坐标是: " + p.y); // 0 0
System.out.println("--------------------------------------");
// 2.将横纵坐标修改为3和5后再次打印
p.x = 3;
p.y = 5;
System.out.println("横坐标是: " + p.x + "纵坐标是: " + p.y); // 3 5
}
}
成员方法
成员方法定义
class 类名{
返回值类型 成员方法名(形参列表){
成员方法体;
}
}
class Person{
void show(){
System.out.println("没事秀一下!");
}
}
注意:
当成员方法名有多个单词组成时,要求从第二个单词起每个词首字母大写
返回值类型详解
1.返回值——指从方法体内返回到方法体外的数据内容
2.返回值类型——主要指返回值的数据类型,可以是基本数据类型,也可以是引用数据类型
3.当返回数据内容是66时,则返回值类型为int
4.在方法体重使用return关键字可以返回具体数据内容并结束当前方法
5.返回66,则方法体中写 return 66;即可
6.该方法不返回任何数据内容时,则返回值类型写void即可
形参列表详解
1.形式参数——将方法体外的数据内容带入到方法体内部
2.形参列表——指多个形式参数组成的列表,语法格式如下:
数据类型 形参变量名1,数据类型 形参变量名2,…
3.带入数据内容为“hello”时,形参列表写 String s 即可
4.带入数据内容为66和“hello”时,形参列表写 int i, String s 即可
5.若该方法不需要带入任何数据内容时,形参列表位置什么也不写即可
方法体详解
1.方法体——主要用于编写描述该方法功能的语句块
参数传递
Person类中无参无返回值成员方法定义
public class Person {
// 数据类型 成员变量名 = 初始值; ————其中 =初始值 通常省略不写
String name; // 描述姓名的成员变量
int age; // 描述年龄的成员变量
// 自定义成员方法实现所有成员变量的打印
// 返回值类型 方法名称(形参列表){ 方法体; }
void show(){
// 成员变量和成员方法都属于类内部的成员,因此可以直接访问成员变量不需要再加引用.的前缀
System.out.println("我是" + name + ", " + "今年" + age + "岁了!");
}
}
Person类中无参无返回值成员方法的调用
引用变量名.成员方法名(实参列表);
实际参数列表主要用于对形式参数列表进行初始化操作,因此参数的个数、类型以及顺序都要完全一致
实际参数可以传递直接量、变量、表达式、方法的调用等
public class Person {
// 数据类型 成员变量名 = 初始值; ————其中 =初始值 通常省略不写
String name; // 描述姓名的成员变量
int age; // 描述年龄的成员变量
// 自定义成员方法实现所有成员变量的打印
// 返回值类型 方法名称(形参列表){ 方法体; }
void show(){
// 成员变量和成员方法都属于类内部的成员,因此可以直接访问成员变量不需要再加引用.的前缀
System.out.println("我是" + name + ", " + "今年" + age + "岁了!");
public static void main(String[] args){
// 1.声明Person类型的引用指向Person类型的对象
// 数据类型(类名) 引用变量名 = new 类名();
Person p = new Person();
// 2.打印对象中的成员变量值
// 引用变量名.成员方法名(实参列表);
// 调用方法的本质就是根据方法名跳转过去执行方法体后再跳转回这个位置
p.show();
System.out.println("------------------------");
// 3.修改成员变量的数值
p.name = "张飞";
p.age = 30;
// 4.再次打印修改后的数值
p.show();
}
}
}
Point类中无参无返回值成员方法定义和调用
public class Point {
int x; // 横坐标
int y; // 纵坐标
// 自定义成员方法实现成员变量数值的打印
void show(){
System.out.println("横坐标是: " + x + "纵坐标是: " + y); // 0 0
}
public static void main(String[] args) {
// 1.声明Point类型引用指向Point类型对象
Point p = new Point();
// 打印成员变量数值
p.show();
System.out.println("--------------------------------------");
// 2.将横纵坐标修改为3和5后再次打印
p.x = 3;
p.y = 5;
p.show();
}
}
Person类中有参无返回值成员方法的使用
public class Person {
// 数据类型 成员变量名 = 初始值; ————其中 =初始值 通常省略不写
String name; // 描述姓名的成员变量
int age; // 描述年龄的成员变量
// 自定义成员方法实现所有成员变量的打印
// 返回值类型 方法名称(形参列表){ 方法体; }
void show(){
// 成员变量和成员方法都属于类内部的成员,因此可以直接访问成员变量不需要再加引用.的前缀
System.out.println("我是" + name + ", " + "今年" + age + "岁了!");
// 自定义成员方法实现将姓名修改为参数指定数值的行为
void setName(String s){
name = s;
}
// 自定义成员方法实现将年龄修改为参数指定数值的行为
void setAge(int i){
age = i;
}
public static void main(String[] args){
// 1.声明Person类型的引用指向Person类型的对象
// 数据类型(类名) 引用变量名 = new 类名();
Person p = new Person();
// 2.打印对象中的成员变量值
// 引用变量名.成员方法名(实参列表);
// 调用方法的本质就是根据方法名跳转过去执行方法体后再跳转回这个位置
p.show();
System.out.println("------------------------");
// 3.修改成员变量的数值
p.name = "张飞";
p.age = 30;
// 4.再次打印修改后的数值
p.show();
System.out.println("------------------------");
// 5.通过成员方法的调用实现成员变量的修改
p.setName("关羽");
p.setAge(35);
p.show(); // 关羽 35
}
}
}
Person类中多个形参成员方法的使用
public class Person {
// 数据类型 成员变量名 = 初始值; ————其中 =初始值 通常省略不写
String name; // 描述姓名的成员变量
int age; // 描述年龄的成员变量
// 自定义成员方法实现所有成员变量的打印
// 返回值类型 方法名称(形参列表){ 方法体; }
void show(){
// 成员变量和成员方法都属于类内部的成员,因此可以直接访问成员变量不需要再加引用.的前缀
System.out.println("我是" + name + ", " + "今年" + age + "岁了!");
// 自定义成员方法实现将姓名修改为参数指定数值的行为
void setName(String s){
name = s;
}
// 自定义成员方法实现将年龄修改为参数指定数值的行为 不推荐使用
void setAge(int i){
age = i;
}
public static void main(String[] args){
// 1.声明Person类型的引用指向Person类型的对象
// 数据类型(类名) 引用变量名 = new 类名();
Person p = new Person();
// 2.打印对象中的成员变量值
// 引用变量名.成员方法名(实参列表);
// 调用方法的本质就是根据方法名跳转过去执行方法体后再跳转回这个位置
p.show();
System.out.println("------------------------");
// 3.修改成员变量的数值
p.name = "张飞";
p.age = 30;
// 4.再次打印修改后的数值
p.show();
System.out.println("------------------------");
// 5.通过成员方法的调用实现成员变量的修改
p.setName("关羽");
p.setAge(35);
p.show(); // 关羽 35
System.out.println("------------------------");
// 6.通过成员方法的调用同时修改姓名和年龄
p.setNameAge("刘备",38);
p.show();
}
}
}
Point类中有参无返回值成员方法的使用
public class Point {
int x; // 横坐标
int y; // 纵坐标
// 自定义成员方法实现成员变量数值的打印
void show(){
System.out.println("横坐标是: " + x + "纵坐标是: " + y); // 0 0
}
public static void main(String[] args) {
// 1.声明Point类型引用指向Point类型对象
Point p = new Point();
// 打印成员变量数值
p.show();
System.out.println("--------------------------------------");
// 2.将横纵坐标修改为3和5后再次打印
p.x = 3;
p.y = 5;
p.show();
System.out.println("--------------------------------------");
// 3.通过调用成员方法实现横纵坐标的修改
p.setX(10);
p.setY(20);
p.show();
}
}
Person类中可变长参数的使用
返回值类型 方法名(参数的类型… 参数名)
方法参数部分指定类型的参数个数是可以改变的,即0~n个
一个方法的形参列表中最多只能声明一个可变长形参,并且要放到参数列表的末尾
public class Person {
// 数据类型 成员变量名 = 初始值; ————其中 =初始值 通常省略不写
String name; // 描述姓名的成员变量
int age; // 描述年龄的成员变量
// 自定义成员方法实现所有成员变量的打印
// 返回值类型 方法名称(形参列表){ 方法体; }
void show(){
// 成员变量和成员方法都属于类内部的成员,因此可以直接访问成员变量不需要再加引用.的前缀
System.out.println("我是" + name + ", " + "今年" + age + "岁了!");
// 自定义成员方法实现将姓名修改为参数指定数值的行为
void setName(String s){
name = s;
}
// 自定义成员方法实现将年龄修改为参数指定数值的行为 不推荐使用
void setAge(int i){
age = i;
}
// 自定义成员方法实现可变长参数的使用 看作一维数组使用即可 0~n个
void ShowArgument(int num,String... args){
System.out.println("num = " + num);
for (int i = 0; i < args.length; i++) {
System.out.println("第" + (i+1) + "个参数为:" + args[i]);
}
}
public static void main(String[] args){
// 1.声明Person类型的引用指向Person类型的对象
// 数据类型(类名) 引用变量名 = new 类名();
Person p = new Person();
// 2.打印对象中的成员变量值
// 引用变量名.成员方法名(实参列表);
// 调用方法的本质就是根据方法名跳转过去执行方法体后再跳转回这个位置
p.show();
System.out.println("------------------------");
// 3.修改成员变量的数值
p.name = "张飞";
p.age = 30;
// 4.再次打印修改后的数值
p.show();
System.out.println("------------------------");
// 5.通过成员方法的调用实现成员变量的修改
p.setName("关羽");
p.setAge(35);
p.show(); // 关羽 35
System.out.println("------------------------");
// 6.通过成员方法的调用同时修改姓名和年龄
p.setNameAge("刘备",38);
p.show();
System.out.println("------------------------");
// 7.通过成员方法的调用实现可变长参数的打印
p.ShowArgument(0);
System.out.println("------------------------");
p.ShowArgument(1,"参数1");
System.out.println("------------------------");
p.ShowArgument(2,"参数1","参数2");
}
}
}
Point类中可变长参数的使用
public class Point {
int x; // 横坐标
int y; // 纵坐标
// 自定义成员方法实现成员变量数值的打印
void show(){
System.out.println("横坐标是: " + x + "纵坐标是: " + y); // 0 0
}
// 自定义成员方法实现int类型的可变长参数使用
void showArgument(int... args){
for (int i = 0; i < args.length; i++) {
System.out.println("下标为" + i + "的元素是:" + args[i]);
}
}
public static void main(String[] args) {
// 1.声明Point类型引用指向Point类型对象
Point p = new Point();
// 打印成员变量数值
p.show();
System.out.println("--------------------------------------");
// 2.将横纵坐标修改为3和5后再次打印
p.x = 3;
p.y = 5;
p.show();
System.out.println("--------------------------------------");
// 3.通过调用成员方法实现横纵坐标的修改
p.setX(10);
p.setY(20);
p.show();
System.out.println("--------------------------------------");
// 4.通过调用成员方法实现可变长参数的使用
p.showArgument(1,2,3,4,5);
}
}
Person类中无参有返回值方法的使用
public class Person {
// 数据类型 成员变量名 = 初始值; ————其中 =初始值 通常省略不写
String name; // 描述姓名的成员变量
int age; // 描述年龄的成员变量
// 自定义成员方法实现所有成员变量的打印
// 返回值类型 方法名称(形参列表){ 方法体; }
void show(){
// 成员变量和成员方法都属于类内部的成员,因此可以直接访问成员变量不需要再加引用.的前缀
System.out.println("我是" + name + ", " + "今年" + age + "岁了!");
// 自定义成员方法实现将姓名修改为参数指定数值的行为
void setName(String s){
name = s;
}
// 自定义成员方法实现将年龄修改为参数指定数值的行为 不推荐使用
void setAge(int i){
age = i;
}
// 自定义成员方法实现可变长参数的使用 看作一维数组使用即可 0~n个
void ShowArgument(int num,String... args){
System.out.println("num = " + num);
for (int i = 0; i < args.length; i++) {
System.out.println("第" + (i+1) + "个参数为:" + args[i]);
}
}
// 自定义成员方法实现姓名数值的获取并返回的行为
String getName(){
return name; // 返回数据并结束方法
// ... 执行不到的
}
// 自定义成员方法实现年龄数值的获取并返回的行为
int getAge(){
return age;
}
public static void main(String[] args){
// 1.声明Person类型的引用指向Person类型的对象
// 数据类型(类名) 引用变量名 = new 类名();
Person p = new Person();
// 2.打印对象中的成员变量值
// 引用变量名.成员方法名(实参列表);
// 调用方法的本质就是根据方法名跳转过去执行方法体后再跳转回这个位置
p.show();
System.out.println("------------------------");
// 3.修改成员变量的数值
p.name = "张飞";
p.age = 30;
// 4.再次打印修改后的数值
p.show();
System.out.println("------------------------");
// 5.通过成员方法的调用实现成员变量的修改
p.setName("关羽");
p.setAge(35);
p.show(); // 关羽 35
System.out.println("------------------------");
// 6.通过成员方法的调用同时修改姓名和年龄
p.setNameAge("刘备",38);
p.show();
System.out.println("------------------------");
// 7.通过成员方法的调用实现可变长参数的打印
p.ShowArgument(0);
System.out.println("------------------------");
p.ShowArgument(1,"参数1");
System.out.println("------------------------");
p.ShowArgument(2,"参数1","参数2");
System.out.println("------------------------");
// 8.通过成员方法的调用实现成员变量数值的获取并打印
String str1 = p.getName();
System.out.println("获取到的姓名是:" + str1); // 刘备
int a = p.getAge();
System.out.println("获取到的年龄是:" + a); // 38
}
}
}
Point类中无参有返回值方法的使用
public class Point {
int x; // 横坐标
int y; // 纵坐标
// 自定义成员方法实现成员变量数值的打印
void show(){
System.out.println("横坐标是: " + x + "纵坐标是: " + y); // 0 0
}
// 自定义成员方法实现int类型的可变长参数使用
void showArgument(int... args){
for (int i = 0; i < args.length; i++) {
System.out.println("下标为" + i + "的元素是:" + args[i]);
}
}
// 自定义成员方法实现获取横坐标数值并返回的行为
int getX(){
return x;
}
// 自定义成员方法实现获取纵坐标数值并返回的行为
int getY(){
return y;
}
public static void main(String[] args) {
// 1.声明Point类型引用指向Point类型对象
Point p = new Point();
// 打印成员变量数值
p.show();
System.out.println("--------------------------------------");
// 2.将横纵坐标修改为3和5后再次打印
p.x = 3;
p.y = 5;
p.show();
System.out.println("--------------------------------------");
// 3.通过调用成员方法实现横纵坐标的修改
p.setX(10);
p.setY(20);
p.show();
System.out.println("--------------------------------------");
// 4.通过调用成员方法实现可变长参数的使用
p.showArgument(1,2,3,4,5);
System.out.println("--------------------------------------");
// 5.通过成员方法的调用实现成员变量数值的获取并打印
int a = p.getX();
System.out.println("获取到的横坐标为:" + a);
int b = p.getY();
System.out.println("获取到的纵坐标为:" + b);
}
}
参数传递注意事项
1.基本数据类型的变量作为方法的参数传递时,形参变量数值的改变不会影响到实参变量的数值,因为两个变量有各自独立的内存空间
2.引用数据类型的变量作为方法的参数传递时,形参变量指向内容的改变会影响到实参变量指向内容的数值,因为两个变量指向同一块内存空间
3.引用数据类型的变量作为方法的参数传递时,若形参变量改变指向后再改变指定的内容,则通常不会影响到实参变量指向内容的改变,因为两个变量指向不同的内存空间
/**
* @auther weiwei
* @date 2021/3/25 14:12
* @description 编程实现参数传递的测试
*/
public class ArgumentTest {
// 自定义成员方法打印参数传入的整数数据
// int ia = ib = 10;
void show1(int ia){
ia = 200;
System.out.println("show方法中: ia = " + ia); //10 200
}
// 自定义成员方法打印参数传入的数组内容
void show2(int[] arr1){
arr1 = new int[2]; //加上该行代码后,相当于在堆区又申请了一块内存空间
arr1[0] = 200;
System.out.println("show2方法中: arr1[0] = " + arr1[0]); // 10 200 200
}
public static void main(String[] args) {
// 1.声明Argumrnt类型的引用指向该类型的对象
ArgumentTest at = new ArgumentTest();
// 2.使用引用变量调用show1方法进行测试
int ib = 10;
at.show1(ib);
System.out.println("main方法中: ib = " + ib); // ib = 10
System.out.println("-------------------------------------");
// 3.调用show2方法进行测试
int[] arr2 = new int[]{10,20};
at.show2(arr2);
System.out.println("main方法中: arr2[0] = " + arr2[0]); // 10 200 10
}
}
内存结构值栈区
1.栈用于存放程序运行过程中的所有局部变量。一个程序运行从开始到结束会有多次方法的调用
2.JVM会为每个方法的调用在栈中分配一个对应的空间,这个空间称为该方法的栈针。一个栈针对应一个正在调用中的方法,栈针中存储了该方法的参数、局部变量等数据
3.当一个方法调用完后,其对应的栈针将被清除
传参相关概念
1.参数分为形参和实参,定义方法时的参数叫形参,调用方法时传递的参数叫实参
2.调用方法时采用值传递把实参传递给形参,方法内部其实是在使用形参
3.值传递即当参数是基本数据类型时,传递参数的值,如 i = 10,真实传参时,把10赋值给了形参。当参数是对象时,传递的是对象的值,即把对象的地址赋值给形参
方法和封装
构造方法(重中之重)
概念和使用
class 类名{
类名(形参列表){
构造方法体;
}
}
class Person{
Person(){ // Person类中的构造方法
}
}
1.构造方法名与类名完全相同并且没有返回值类型,连void都不许有
2.当一个类中没有定义任何构造方法时,编译器会自动添加一个无参空构造方法,叫做默认/缺省构造方法,如Person(){}
3.若类中出现了构造方法,则编译器不再提供任何形式的构造方法
/**
* @auther weiwei
* @date 2021/3/25 16:31
* @description 编程实现Person类的定义
*/
public class Person {
String name; // 描述姓名的成员变量
int age; // 描述年龄的成员变量
// 自定义构造方法
Person(){
System.out.println("我就是自定义的构造方法");
}
// 自定义成员方法实现所有成员特征的打印
void show(){
System.out.println("我是" + name + "," + "今年" + age + "岁了!");
}
public static void main(String[] args) {
// 1.声明一个Person类型的引用指向Person类型的对象
// 当类中没有提供构造方法时,下面调用默认构造方法,若类中提供构造方法后,则下面调用类中提供的版本
Person p1 = new Person();
// 并打印特征
p1.show(); // null 0
}
}
构造方法作用
使用new关键字创建对象时会自动调用构造方法实现成员变量初始化工作
public class Person {
String name; // 描述姓名的成员变量
int age; // 描述年龄的成员变量
// 自定义构造方法
Person(String s, int i){
//System.out.println("我就是自定义的构造方法");
//name = "张飞";
//age = 30;
name = s;
age = i;
}
// 自定义无参构造方法
Person(){}
// 自定义成员方法实现所有成员特征的打印
void show(){
System.out.println("我是" + name + "," + "今年" + age + "岁了!");
}
public static void main(String[] args) {
// 1.声明一个Person类型的引用指向Person类型的对象
// 当类中没有提供构造方法时,下面调用默认构造方法,若类中提供构造方法后,则下面调用类中提供的版本
Person p1 = new Person("张飞",30);
// 并打印特征
p1.show(); // null 0 张飞 30
Person p2 = new Person("关羽",35);
p2.show(); // 关羽 35
Person p3 = new Person();
p3.show(); // null 0
}
}
Point类的定义
/**
* @auther weiwei
* @date 2021/3/25 16:56
* @description 编程实现Pointlei的定义
*/
public class Point {
int x; // 横坐标变量
int y; // 纵坐标变量
// 自定义无参构造方法
Point(){}
// 自定义有参构造方法
Point(int i, int j){
x = i;
y = j;
}
// 自定义成员方法实现特征的打印
void show(){
System.out.println("横坐标是:" + x + "纵坐标是:" + y);
}
public static void main(String[] args) {
// 1.使用无参构造方式构造对象并打印特征
Point p1 = new Point();
p1.show(); // 0 0
// 2.使用有参构造方式构造对象并打印特征
Point p2 = new Point(2,3);
p2.show(); // 2 3
}
}
重载的概念和体现形式(重点)
概念
若方法名相同,参数列表不同,这样的方法之间构成重载关系(Overload)
体现形式
参数的个数不同、参数的类型不同、参数的顺序不同,与返回值的类型和形参变量名无关,但建议返回值类型最好相同
/**
* @auther weiwei
* @date 2021/3/25 17:08
* @description 编程实现方法重载主要形式的测试
*/
public class OverloadTest {
// 自定义成员方法
void show(){
System.out.println("show()");
}
void show(int i){ // ok 体现在方法参数的个数不同
System.out.println("show(int)");
}
void show(int i, double d){ // ok 体现在方法参数的个数不同
System.out.println("show(int,double)");
}
void show(int i, int j){ // ok 体现在方法参数的类型不同
System.out.println("show(int,int)");
}
void show(double d, int i){ // ok 体现在方法参数的顺序不同
System.out.println("show(double,int)");
}
// void show(double a, int b){ //方法重载与参数变量名无关
// System.out.println("show(double,int)");
// }
// int show(double d, int i){ //方法重载与返回值类型无关
// System.out.println("show(double,int)");
// }
public static void main(String[] args) {
// 1.声明Overloa类型的引用指向该类型的对象
OverloadTest ot = new OverloadTest();
// 2.调用show方法
ot.show();
ot.show(66);
ot.show(66,3.14);
ot.show(66,118);
ot.show(3.14,118);
//ot.show(3.14,66);
}
}
Person类中重载的使用
/**
* @auther weiwei
* @date 2021/3/25 16:31
* @description 编程实现Person类的定义
*/
public class Person {
String name; // 描述姓名的成员变量
int age; // 描述年龄的成员变量
// 自定义构造方法
Person(String s, int i){
//System.out.println("我就是自定义的构造方法");
//name = "张飞";
//age = 30;
name = s;
age = i;
}
// 自定义无参构造方法
Person(){}
// 自定义成员方法实现所有成员特征的打印
void show(){
System.out.println("我是" + name + "," + "今年" + age + "岁了!");
}
// 自定义成员方法实现年龄增长一岁的行为
void grow(){
age++;
}
// 自定义成员方法实现年龄增长参数指定数值的行为
void grow(int i){
age += i;
}
public static void main(String[] args) {
// 1.声明一个Person类型的引用指向Person类型的对象
// 当类中没有提供构造方法时,下面调用默认构造方法,若类中提供构造方法后,则下面调用类中提供的版本
Person p1 = new Person("张飞",30);
// 并打印特征
p1.show(); // null 0 张飞 30
Person p2 = new Person("关羽",35);
p2.show(); // 关羽 35
Person p3 = new Person();
p3.show(); // null 0
// 2.实现重载方法的调用
System.out.println("--------------------");
p1.grow();
p1.show(); // 张飞 31
p1.grow(3);
p1.show(); // 张飞 34
}
}
Point类中重载的使用
public class Point {
int x; // 横坐标变量
int y; // 纵坐标变量
// 自定义无参构造方法
Point(){}
// 自定义有参构造方法
Point(int i, int j){
x = i;
y = j;
}
// 自定义成员方法实现特征的打印
void show(){
System.out.println("横坐标是:" + x + "纵坐标是:" + y);
}
// 自定义成员方法实现纵坐标减1的行为
void up(){
y -= 1;
}
// 自定义成员方法实现纵坐标减去参数指定数值的行为
void up(int dy){
y -= dy;
}
public static void main(String[] args) {
// 1.使用无参构造方式构造对象并打印特征
Point p1 = new Point();
p1.show(); // 0 0
// 2.使用有参构造方式构造对象并打印特征
Point p2 = new Point(3,5);
p2.show(); // 3 5
System.out.println("---------------------------------");
// 3.调用重载的成员方法
p2.up();
p2.show(); // 3 4
p2.up(2);
p2.show(); // 3 2
}
}
重载的实际意义
调用者只需要记住一个方法名就可以调用各种不同的版本,来实现不同的功能
this关键字(原理)
基本概念
1.若在构造方法中出现了this关键字,则代表当前正在构造对象
2.若在成员方法中出现了this关键字,则代表当前正在调用对象
3.this关键字本质上就是当前类类型的引用变量
/**
* @auther weiwei
* @date 2021/3/25 20:06
* @description 编程实现this关键字的使用
*/
public class ThisTest {
// 自定义构造方法
ThisTest(){
// this代表当前正在构造的对象
System.out.println("构造方法中:this = " + this);
}
void show(){
// this代表当前正在调用的对象
System.out.println("成员方法中:this = " + this);
}
public static void main(String[] args) {
// 1.声明ThisTest类型的引用指向该类型的对象
ThisTest tt = new ThisTest();
// 2.调用show方法
tt.show();
System.out.println("main方法中: tt = " + tt);
}
}
工作原理
在构造方法中和成员方法中访问成员变量时,编译器会加上this.的前缀,而this.相当于汉语中“我的”,当不同对象调用同一个方法时,由于调用方法的对象不同而导致this关键字不同,从而this方式的结果也就随之不同
this关键字的使用方式
1.当局部变量与成员变量名相同时,在方法体中会优先使用局部变量(就近原则)。若希望使用成员变量,则需要在成员变量的前面加上this.的前缀,明确要求该变量时成员变量。(重中之重)
2.this关键字除了可以通过this.的方式调用成员变量和成员方法外,还可以作为方法的返回值。(重中之重)
// 自定义成员方法实现所有成员特征的打印
void show(){
// 每当打印成员变量的数值时,让年龄增长一岁
//this.grow();
grow();
System.out.println("我是" + name + "," + "今年" + age + "岁了!");
}
// 自定义成员方法实现年龄增长一岁的行为
void grow(){
age++;
}
//this关键字作为返回值
//自定义成员方法实现Person类型对象的获取并返回的行为
//String getName(){}
Person getPerson(){
// 返回当前调用对象本身 Person tp = new Person(); return tp;
return this;
}
3.在构造方法的第一行可以使用this()的方式来调用本类中的其他构造方法。(了解)
/**
* @auther weiwei
* @date 2021/3/28 10:34
* @description 编程实现Boy类的定义
*/
public class Boy {
String name; // 描述姓名的成员变量
// 自定义构造方法
Boy(){
// 调用本类中的有参构造方法
//this("无名");
System.out.println("无参构造方法!");
}
Boy(String name){
// 调用本类中的无参构造方法
this();
System.out.println("=======有参构造方法!");
this.name = name;
}
// 自定义成员方法实现特征的打印
void show(){
System.out.println("我的名字是:" + name);
}
public static void main(String[] args) {
// 1.使用无参方法方式构造并打印特征
Boy b1 = new Boy();
b1.show();
System.out.println("------------------------------");
// 2.使用有参方式构造对象并打印特征
Boy b2 = new Boy("张飞");
b2.show();
}
}
引用变量注意事项
1.引用类型变量用于存放对象的地址,可以给引用类型赋值为null,表示不指向任何对象
2.当某个引用类型变量为null时无法对对象实施访问(因为它没有执行任何对象)。此时,如果通过引用访问成员变量或调用方法,会产生NullPointerException异常。
阶乘的计算
方法一----递推
/**
* @auther weiwei
* @date 2021/3/28 10:58
* @description 编程实现阶乘的计算并打印
*/
public class JieChengTest {
// 自定义成员方法实现将参数n的阶乘计算出来并返回
// 1! = 1; 2! = 1*2; 3! = 1*2*3; 4! = 1*2*3*4; ... n! = 1*2*3 ... *n;
int show(int n){
// 递推
int num = 1;
for (int i = 1; i <= n; i++) {
num *= i;
}
return num;
}
public static void main(String[] args) {
// 1.声明JieChengTest类型的引用指向该类型的对象
JieChengTest jct = new JieChengTest();
// 2.调用方法进行计算并打印
int res = jct.show(5);
System.out.println("最终计算结果是:" + res);
}
}
方法二----递归(难点)
/**
* @auther weiwei
* @date 2021/3/28 10:58
* @description 编程实现阶乘的计算并打印
*/
public class JieChengTest {
// 自定义成员方法实现将参数n的阶乘计算出来并返回
// 1! = 1; 2! = 1*2; 3! = 1*2*3; 4! = 1*2*3*4; ... n! = 1*2*3 ... *n;
int show(int n) {
/*
5! = 5 * 4 * 3 * 2 * 1;
4! = 4 * 3 * 2 * 1;
3! = 3 * 2 * 1;
2! = 2 * 1;
1! = 1;
5! = 5 * 4!;
4! = 4 * 3!;
3! = 3 * 2!;
2! = 2 * 1;
1! = 1;
n! = n * (n-1)!;
*/
// 当n的数值为1时,则阶乘的结果就是1
if(1 == n){
return 1;
}
// 否则阶乘的结果就是 n*(n-1)!
return n*show(n-1);
}
public static void main(String[] args) {
// 1.声明JieChengTest类型的引用指向该类型的对象
JieChengTest jct = new JieChengTest();
// 2.调用方法进行计算并打印
int res = jct.show(5);
System.out.println("最终计算结果是:" + res);
}
}
注意事项
1.使用递归必须有递归的规律以及退出条件
2.使用递归必须使得问题简单化而不是复杂化
3.若递归影响到程序的执行性能,则使用递推取代之
费氏数列
递归实现
费氏数列:1 1 2 3 5 8 13 21 …
/**
* @auther weiwei
* @date 2021/3/28 11:29
* @description 编程实现费氏数列的计算并打印
*/
public class FeeTest {
// 自定义成员方法实现费氏数列中第n项数值的计算并返回,n由参数指定
// 1 1 2 3 5 8 13 21 ...
int show(int n){
// 1.使用递归的方式进行计算
// 当n=1或者n=2时,结果是1
if(1 == n || 2 == n){
return 1;
}
// 否则结果是前两项的和
return show(n-1) + show(n-2);
}
public static void main(String[] args) {
// 1.声明FeeTest类型的引用指向该类型的对象
FeeTest ft = new FeeTest();
// 2.调用方法计算并打印
int res = ft.show(8);
System.out.println("计算的结果是:" + res); //5
}
}
递推实现
/**
* @auther weiwei
* @date 2021/3/28 11:29
* @description 编程实现费氏数列的计算并打印
*/
public class FeeTest {
// 自定义成员方法实现费氏数列中第n项数值的计算并返回,n由参数指定
// 1 1 2 3 5 8 13 21 ...
int show(int n){
// 2.使用递推的方式进行计算
int ia = 1;
int ib = 1;
for (int i = 3; i <= n; i++) {
int ic = ia + ib;
ia = ib;
ib = ic;
}
return ib;
}
public static void main(String[] args) {
// 1.声明FeeTest类型的引用指向该类型的对象
FeeTest ft = new FeeTest();
// 2.调用方法计算并打印
int res = ft.show(55);
System.out.println("计算的结果是:" + res); //5
}
}
封装(重中之重)
概念
通常情况下在测试类给成员变量赋值一些合法但不合理的数值,无论是编译阶段还是运行阶段都不会报错或者给出提示,此时与现实生活不符。
为了避免上述错误的发生,就需要度成员变量进行密封包装处理,来隐藏成员变量的细节以及保证变量数值的合理性,该机制就叫封装。
封装的实现
1.私有化成员变量,使用private关键字修饰
2.提供公有的get和set方法,并子啊方法体中进行合理值的判断
3.在构造方法中调用set方法进行合理值的判断
/**
* @auther weiwei
* @date 2021/3/28 12:17
* @description 编程实现Student类的封装----功能类
*/
public class Student {
// 1.私有化成员变量,使用private关键字
// private关键字表示私有的含义,即该成员变量只能在当前类的内部使用
private int id; // 描述学号的成员变量
private String name; // 描述姓名的成员变量
// 自定义构造方法
public Student(){}
public Student(int id, String name) {
//this.id = id;
//this.name = name;
this.setId(id);
this.setName(name);
}
// 2.提供公有的get和set方法,并在方法体中进行合理值得判断
// 使用public关键字修饰表示公有的含义,即该方法可以再任意位置使用
public int getId() {
return id;
}
public void setId(int id) {
if(id > 0){
this.id = id;
}else{
System.out.println("学号不合理!");
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// 自定义成员方法实现特征的打印
// 什么修饰符都没有叫做默认的访问权限,级别介于private和public之间
public void show(){
//System.out.println("我是" + name + ",我的学号是" + id);
System.out.println("我是" + getName() + ",我的学号是" + getId());
}
}
/**
* @auther weiwei
* @date 2021/3/28 12:19
* @description 编程实现Student类的测试----测试类
*/
public class StudentTest {
public static void main(String[] args) {
// 1.声明Student类的引用指向该类的对象
Student s1 = new Student();
// 2.对成员变量进行复制并打印
//s1.id = -1001;
//s1.name = "张飞";
s1.setId(-1001);
s1.setName("张飞");
s1.show(); // 张飞 1001
System.out.println("-----------------------");
// 3.使用有参方式构造对象并打印
Student s2 = new Student(-1001,"张飞");
s2.show();
}
}
学生信息录入
import java.util.Scanner;
/**
* @auther weiwei
* @date 2021/3/28 13:44
* @description 编程实现学生信息的录入和打印
*/
public class StudentTest2 {
public static void main(String[] args) {
// 1.提示用户输入学生的人数并使用变量记录
System.out.println("请输入学生人数: ");
Scanner sc = new Scanner(System.in);
int num = sc.nextInt();
// 2.根据学生人数准备对应的一维数组
// int[] arr = new int[3]; - 表示声明一个长度为3元素类型为int类型的一维数组
// 数组中的每个元素都是int类型,也就说数组中的每个元素都可看做是一个int类型的变量,使用整数数据进行初始化 arr[0] = 10;
// 下面代码是声明一个长度为num的Student类型的一维数组
// 数组中的每个元素都是Student类型,也就是说数组中的每一个元素都可以看做Student类型的变量,arr[0] = new Student();
Student[] arr = new Student[num];
// 3.提示用户输入每个学生的信息(学号,姓名)并记录到一维数组中
for (int i = 0; i < num; i++) {
System.out.println("请输入第" + (i+1) + "个学生的信息(学号 姓名): ");
arr[i] = new Student(sc.nextInt(),sc.next());
}
// 4.打印所有学生信息
System.out.println("该班级的所有学生信息有: ");
for (int i = 0; i < num; i++) {
//System.out.println(arr[i]);
arr[i].show();
}
}
}
JavaBean概念(了解)
JavaBean是一种Java语言写成的可重用组件,其他Java类可以通过反射机制发现和操作这些JavaBean的属性。
JavaBean本质上就是符合以下标准的类:
1.类是公共的
2.有一个无参的公共构造器
3.有属性,且有对应的get、set方法
Static关键字和继承
People类和测试类的实现
/**
* @auther weiwei
* @date 2021/3/28 14:11
* @description 编程实现People类的封装
*/
public class People {
// 1.私有化成员变量,使用private关键字修饰
private String name;
private int age;
private String nation;
// 在构造方法中调用set方法进行合理值判断
public People(){}
public People(String name, int age, String nation) {
setName(name);
setAge(age);
setNation(nation);
}
// 2.提供公有的get和set方法,并咋方法体中进行合理值判断
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if(age > 0 && age < 150){
this.age = age;
}else{
System.out.println("年龄不合理!");
}
}
public String getNation() {
return nation;
}
public void setNation(String nation) {
this.nation = nation;
}
public void show(){
System.out.println("我叫" + getName() + ", 今年" + getAge() + "岁了, " + "来自" + getNation());
}
}
/**
* @auther weiwei
* @date 2021/3/28 14:15
* @description 编程实现People类的测试
*/
public class PeopleTest {
public static void main(String[] args) {
// 1.使用有参方式构造两个People类型的对象并打印特征
People p1 = new People("张飞",30,"中国");
p1.show();
People p2 = new People("麦克",24,"美国");
p2.show();
}
}
static关键字
概念
使用static关键字修饰成员变量表示静态含义,此时成员变量由对象层级提升为类层级,即整个类只有一份并被所有类共享,该成员变量随着类的加载准备就绪,与是否创建对象无关。
static关键字修饰成员可以使用 引用. 的方式访问,但推荐 类名. 的方式
//private String nation; // 隶属于对象层级,即每个对象都要独立的一份
public static String nation; // 隶属于类层级,即整个类只有一份并且被所有对象共享
/**
* @auther weiwei
* @date 2021/3/28 14:15
* @description 编程实现People类的测试
*/
public class PeopleTest {
public static void main(String[] args) {
// 2.验证static关键字修饰的静态成员(类成员)是否与创建对象无关
System.out.println("获取到的国籍信息是:" + People.nation); // null
// 1.使用有参方式构造两个People类型的对象并打印特征
People p1 = new People("张飞",30,"中国");
p1.show();
People p2 = new People("关羽",24,"中国");
p2.show();
System.out.println("--------------------------------------");
// 2.验证static关键字修饰的静态成员(类成员)是否被所有对象共享 =》 共享
p1.nation = "蜀国";
System.out.println("第一个对象的国籍是:" + p1.nation); // 蜀国
System.out.println("第二个对象的国籍是:" + p2.nation); // 蜀国
People p3 = new People();
System.out.println("第三个对象的国籍是:" + p3.nation);
}
}
使用方式
1.在非静态成员方法中既能访问非静态的成员又能访问静态的成员。
(成员:成员变量 + 成员方法,静态成员被所有对象共享)
2.静态成员方法中只能访问静态成员不能访问非静态成员。
(成员:成员变量 + 成员方法,因此此时可能还没有创建对象)
3.在以后的开发中只有隶属于类层级并被所有对象共享的内容才可以使用static关键字修饰。(不能滥用static关键字)
/**
* @auther weiwei
* @date 2021/3/28 15:41
* @description 编程实现static关键字的使用
*/
public class StaticTest {
private int cnt = 1; // 隶属于对象层级,也即每个对象都拥有独立一份
private static int snt = 2; //隶属于类层级,即所有对象共享同一份
// 自定义非静态成员方法 需要使用 引用. 的方式访问
public void show(){
System.out.println("cnt = " + this.cnt); // 1
System.out.println("snt = " + this.snt); // 2 静态成员被所有对象共享,this关键字可以省略
}
// 自定义静态的成员方法 推荐使用 类名. 的方式访问
public static void test(){
//System.out.println("cnt = " + cnt); // 1 静态成员方法中没有this关键字,因为是通过 类名. 方式调用的
System.out.println("snt = " + snt); // 2
}
public static void main(String[] args) {
StaticTest st = new StaticTest();
st.show();
System.out.println("-------------------------");
StaticTest.test();
}
}
构造块和静态代码块(熟悉)
构造块:在类体中直接使用{}括起来的代码块
每创建一个对象都会执行一次构造块
静态代码块:使用static关键字修饰的构造块
静态代码块随着类加载时执行一次
/**
* @auther weiwei
* @date 2021/3/28 20:36
* @description 编程实现构造块和静态代码块的使用
*/
public class BlockTest {
// 当需要在执行构造方法之前做一些准备工作时,则将准备工作的相关代码写在构造块中即可,比如:对成员变量进行初始化
{
System.out.println("构造块!"); // (2)
}
// 静态代码块会随着类的加载而准备就绪,会先于构造块执行
// 当需要在执行代码块之前随着类的加载做一些准备工作时,则编写代码到静态代码块中,比如:加载数据库的驱动包
static{
System.out.println("####静态代码块!"); // (1)
}
// 自定义构造方法
public BlockTest(){
System.out.println("====构造方法体!"); // (3)
}
public static void main(String[] args) {
BlockTest bt = new BlockTest();
BlockTest bt2 = new BlockTest();
}
}
main方法详解
/**
* @auther weiwei
* @date 2021/3/30 12:59
* @description 编程实现main方法的测试
*/
public class MainTest {
public static void main(String[] args) {
System.out.println("参数数组中元素的个数是:" + args.length);
System.out.println("传递给main方法的实际参数为:");
for (int i = 0; i < args.length; i++) {
System.out.println("下标为" + i + "的形参变量数值为: " + args[i]);
}
}
}
单例设计模式
在某些特殊场合中,一个类对外提供且只提供一个对象时,这样的类叫做单例类,二设计单例的流程和思想叫做单例设计模式。
实现流程
1.私有化构造方法,使用private关键字修饰
2.声明本类类型的引用指向本类类型的对象,并使用private static关键字共同修饰
3.提供公有的get方法赋值将对象返回出去,并使用public static关键字共同修饰
实现方式
1.饿汉式----推荐使用
2.懒汉式
Singlton和SingletonTest类的实现(重中之重)
/**
* @auther weiwei
* @date 2021/3/30 13:15
* @description 编程实现Singleton类的封装
*/
public class Singleton {
// 2.声明本类类型的引用指向本类类型的对象,使用private static关键字共同修饰
//private static Singleton sin = new Singleton(); //饿汉式
private static Singleton sin = null; // 懒汉式
// 1.私有化构造方法
private Singleton(){}
// 3.提供公有的get方法负责将对象返回出去,使用public static关键字共同修饰
public static Singleton getInstance(){
//return sin;
if(null == sin){
sin = new Singleton();
}
return sin;
}
}
/**
* @auther weiwei
* @date 2021/3/30 13:15
* @description 编程实现Singleton类的测试
*/
public class SingletonTest {
public static void main(String[] args) {
// 1.声明Singleton类型的引用指向该类型的对象
//Singleton s1 = new Singleton();
//Singleton s2 = new Singleton();
//System.out.println(s1 == s2); // 比较变量s1的数值是否与变量s2的数值相等 false
//Singleton.sin = null; 可以使得引用变量无效
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2); //true
}
}
继承
概念意义
当多个类之间有相同的特征和行为时,可以将相同的内容提取出来组成一个公共类,让多个类吸收公共类中已有特征和行为而在多个类型只需要编写自己独有特征和行为的机制,叫做继承。
Java中用extends(扩展)关键字表示继承关系
使用继承提高了代码的复用性,可维护性及扩展性,是多态的前提条件
/**
* @auther weiwei
* @date 2021/3/30 16:31
* @description 编程实现Person类的封装
*/
public class Person {
// 1.私有化成员变量
private String name;
private int age;
//private boolean gender; //性别
// 3.在构造方法中调用set方法进行合理值的判断
public Person(){}
public Person(String name, int age){
setName(name);
setAge(age);
}
// 2.提供公有的get和set方法,并在方法体中进行合理值判断
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public int getAge(){
return age;
}
public void setAge(int age){
if(age > 0 || age < 150){
this.age = age;
}else{
System.out.println("年龄不合理!");
}
}
// 自定义成员方法实现特征的打印
public void show(){
System.out.println("我是" + getName() + ", 今年" + getAge() + "岁了!");
}
// 自定义成员方法描述吃饭的行为
public void eat(String food){
System.out.println(food + "真好吃!");
}
// 自定义成员方法描述娱乐的行为
public void play(String game){
System.out.println(game + "真好玩!");
}
}
public class Worker extends Person{}
public class WorkerTest {
public static void main(String[] args) {
// 1.使用无参方式构造Worker类型的对象并打印特征
Worker w1 = new Worker();
w1.show(); // null 0
}
}
public class Teacher extends Person{}
public class TeacherTest {
public static void main(String[] args) {
// 1.使用无参方式构造Teacher类型的对象并打印特征
Teacher t1 = new Teacher();
t1.show(); // null 0
}
}
继承的特点
1.子类不能继承父类的构造方法和私有方法,但私有成员变量可以被继承只是不能直接访问。
2.无论用何种方式构造子类的对象时都会自动调用父类的无参构造方法,来初始化从父类中继承的成员变量,相当于在构造方法的第一行增加代码super()的效果。
3.使用继承必须满足逻辑关系:子类 is a 父类,也就是不能滥用继承
4.Java语言中只支持单继承不支持多继承,也就是说一个子类只能有一个父类,但是一个父类可以有多个子类
/**
* @auther weiwei
* @date 2021/3/30 16:31
* @description 编程实现Person类的封装
*/
public class Person {
// 1.私有化成员变量
private String name;
private int age;
//private boolean gender; //性别
// 3.在构造方法中调用set方法进行合理值的判断
public Person(){
System.out.println("Person()");
}
public Person(String name, int age){
System.out.println("Person(String, int)");
setName(name);
setAge(age);
}
// 2.提供公有的get和set方法,并在方法体中进行合理值判断
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public int getAge(){
return age;
}
public void setAge(int age){
if(age > 0 || age < 150){
this.age = age;
}else{
System.out.println("年龄不合理!");
}
}
// 自定义成员方法实现特征的打印
public void show(){
System.out.println("我是" + getName() + ", 今年" + getAge() + "岁了!");
}
// 自定义成员方法描述吃饭的行为
public void eat(String food){
System.out.println(food + "真好吃!");
}
// 自定义成员方法描述娱乐的行为
public void play(String game){
System.out.println(game + "真好玩!");
}
}
/**
* @auther weiwei
* @date 2021/3/30 16:42
* @description 自定义Worker类继承自Person类
*/
public class Worker extends Person{
private int salary;
public Worker(){
super(); //表示调用父类的无参构造方法,若没有加则编译器自动添加
System.out.println("Worker()");
}
public Worker(String name, int age, int salary){
super(name, age); //表示调用父类的有参构造方法
System.out.println("Worker(String, int, int)");
//setName(name);
//setAge(age);
setSalary(salary);
}
public int getSalary(){
return salary;
}
public void setSalary(int salary){
if(salary >= 2200){
this.salary = salary;
}else{
System.out.println("薪水不合理!");
}
}
// 自定义成员方法描述工作的行为
public void work(){
System.out.println("今天的砖优点烫手!");
}
}
/**
* @auther weiwei
* @date 2021/3/30 16:43
* @description 编程实现Worker类的测试
*/
public class WorkerTest {
public static void main(String[] args) {
// 1.使用无参方式构造Worker类型的对象并打印特征
Worker w1 = new Worker();
w1.show(); // null 0
System.out.println("-----------------------");
// 2.使用有参方式构造Worker类型的对象把那个打印特征
Worker w2 = new Worker("张飞",30,3000);
w2.show(); // 张飞 30
// 调用成员方法测试
w2.eat("豆芽");
w2.play("王者");
w2.work();
}
}
/**
* @auther weiwei
* @date 2021/3/30 16:49
* @description 编程实现Teacher类继承自Person类
*/
public class Teacher extends Person{}
/**
* @auther weiwei
* @date 2021/3/30 16:50
* @description 编程实现Teacher类的测试
*/
public class TeacherTest {
public static void main(String[] args) {
// 1.使用无参方式构造Teacher类型的对象并打印特征
Teacher t1 = new Teacher();
t1.show(); // null 0
}
}
方法的重写
从父类中继承下来的方法不满足子类的需求时,就需要在子类中重新写一个和父类一样的方法来覆盖从父类中继承下来的版本,该方式叫做方法的重写(Override)。
/**
* @auther weiwei
* @date 2021/3/30 16:42
* @description 自定义Worker类继承自Person类
*/
public class Worker extends Person{
private int salary;
public Worker(){
super(); //表示调用父类的无参构造方法,若没有加则编译器自动添加
System.out.println("Worker()");
}
public Worker(String name, int age, int salary){
super(name, age); //表示调用父类的有参构造方法
System.out.println("Worker(String, int, int)");
//setName(name);
//setAge(age);
setSalary(salary);
}
public int getSalary(){
return salary;
}
public void setSalary(int salary){
if(salary >= 2200){
this.salary = salary;
}else{
System.out.println("薪水不合理!");
}
}
// 自定义成员方法描述工作的行为
public void work(){
System.out.println("今天的砖有点烫手!");
}
// 自定义show方法覆盖从父类中继承的版本
@Override // 标注/注解,用于说明下面方法是对父类方法的重写,若没有构成重写则编译报错
public void show() {
super.show(); // 调用父类的show方法
System.out.println("我的薪水是: " + getSalary());
}
}
/**
* @auther weiwei
* @date 2021/3/30 16:43
* @description 编程实现Worker类的测试
*/
public class WorkerTest {
public static void main(String[] args) {
// 1.使用无参方式构造Worker类型的对象并打印特征
Worker w1 = new Worker();
// 当子类重写show方法后,则下面调用的是重写以后的版本
w1.show(); // null 0
System.out.println("-----------------------");
// 2.使用有参方式构造Worker类型的对象把那个打印特征
Worker w2 = new Worker("张飞",30,3000);
w2.show(); // 张飞 30
// 调用成员方法测试
w2.eat("豆芽");
w2.play("王者");
w2.work();
}
}
方法重写的原则
1.要求方法名相同、参数列表相同以及返回值类型相同,从Java5开始允许返回子类类型
2.要求方法的访问权限不能变小,可以相同或者变大
3.要求方法不能抛出更大的异常(异常机制)
Animal类,Dog类和DogTest类的实现
/**
* @auther weiwei
* @date 2021/3/30 19:33
* @description 编程实现Animal类的封装
*/
public class Animal {
// 1.私有化变量
private String name;
private String color;
// 3.在构造方法中调用set方法进行合理值的判断
public Animal(){}
public Animal(String name, String color) {
this.name = name;
this.color = color;
}
// 2.提供公有的get和set方法,并在方法体中进行合理值判断
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
// 自定义成员方法打印所有特征
public void show(){
System.out.println("名字:" + getName() + ", 颜色" + getColor());
}
}
/**
* @auther weiwei
* @date 2021/3/30 19:44
* @description 编程实现Dog类的封装
*/
public class Dog extends Animal{
private int tooth;
public Dog(){
super(); // 表示调用父类的无参构造方法
}
public Dog(String name, String color, int tooth) {
super(name, color); // 表示调用父类的有参构造方法
setTooth(tooth);
}
public int getTooth() {
return tooth;
}
public void setTooth(int tooth) {
if (tooth > 0){
this.tooth = tooth;
}else{
System.out.println("牙齿数量不合理哟!");
}
}
// 对父类方法进行重写
@Override
public void show() {
super.show();
System.out.println("牙齿的数量是: " + getTooth());
}
}
/**
* @auther weiwei
* @date 2021/3/30 19:56
* @description 编程实现Dog类的测试
*/
public class DogTest {
public static void main(String[] args) {
// 1.使用无参方式构建Dog类型的对象并打印特征
Dog d1 = new Dog();
d1.show(); // null null 0
System.out.println("---------------------------------");
// 2.使用有参方式构建Dog类型的对象并打印特征
Dog d2 = new Dog("旺财","白色",10);
d2.show(); // 旺财 白色 10
}
}
构造块和静态代码块考点(笔试)
1.先执父类的静态代码块,再执行子类的静态代码块
2.执行父类的构造块,执行父类的构造方法体
3.执行子类的构造块,执行子类的构造方法体
/**
* @auther weiwei
* @date 2021/3/30 20:02
* @description 构造块和静态代码块的考点
*/
public class SuperTest {
{
System.out.println("SuperTest中的构造块"); // (2) c
}
static{
System.out.println("SuperTest中的静态构造块"); // (1) a
}
public SuperTest(){
System.out.println("SuperTest中的构造方法体"); // (3) d
}
public static void main(String[] args) {
// 使用无参方式构造对象
SuperTest st = new SuperTest();
}
}
public class SubSuperTest extends SuperTest{
{
System.out.println("=====SubTest中的构造块"); // (2) e
}
static{
System.out.println("=====SubTest中的静态构造块"); // (1) b
}
public SubSuperTest(){
System.out.println("=====SubTest中的构造方法体"); // (3) f
}
public static void main(String[] args) {
// 使用无参方式构造子类对象
SubSuperTest sst = new SubSuperTest();
}
}
权限修饰符和包
常用访问控制符
修饰符 | 本类 | 同一个包中的类 | 子类 | 其他类 |
public | 可以访问 | 可以访问 | 可以访问 | 可以访问 |
protected | 可以访问 | 可以访问 | 可以访问 | 不能访问 |
default(默认) | 可以访问 | 可以访问 | 不能访问 | 不能访问 |
private | 可以访问 | 不能访问 | 不能访问 | 不能访问 |
注意事项
1.public修饰的成员可以再任意位置使用
2.private修饰的成员只能在本类内部使用
3.通常情况下,成员方法都使用public修饰,成员变量都使用private修饰
package
在Java中,用包的概念来解决命名冲突的问题
可以实现项目管理、解决命名冲突以及权限控制的效果
包的定义:
package 包名;
package 包名1.包名2.包名3…包名n;
定义包的规范
org.apache.commons.lang.StringUtils
----其中StringUtils是类名而org.apache.commons.lang是多层包名,其含义是:
org.apache表示公司或组织的信息(是这个公司(或组织)域名的反写)
commons表示项目的名称信息
lang表示模块的名称信息
包的导入
1.使用import关键字导入包
2.从Java5.0开始,支持使用import关键字导入静态成员(很少使用)
// 导入java目录中lang目录中System类中的静态成员out
import static java.lang.System.out;
/**
* @auther weiwei
* @date 2021/3/30 20:06
* @description
*/
public class SubSuperTest extends SuperTest{
{
System.out.println("=====SubTest中的构造块"); // (2) e
}
static{
System.out.println("=====SubTest中的静态构造块"); // (1) b
}
public SubSuperTest(){
//System.out.println("=====SubTest中的构造方法体"); // (3) f
out.println("=====SubTest中的构造方法体");
}
public static void main(String[] args) {
// 使用无参方式构造子类对象
SubSuperTest sst = new SubSuperTest();
}
}
final关键字
概念
final本意为“最终的、不可改变的”,可以修饰类、成员方法以及成员变量
修饰类、方法和成员变量的作用
1.final关键字修饰类体现在该类不能被继承
主要用于防止滥用继承,如:java.lang.String类等
2.final关键字修饰成员方法体现在该方法不能被重写但可以被继承
主要用于防止不经意间造成重写,如:java.text.Dateformat类中format方法等
// ctrl+shift+/----选中内容的多行注释,再按一次取消
// ctrl+/----当前行单行注释
public /*final*/ class FinalClass {
public final void show(){
System.out.println("FinalClass类中的show方法!");
}
}
public class SubFinalClass extends FinalClass{
/*@Override
public void show() {
super.show();
}*/
public static void main(String[] args) {
SubFinalClass sfc = new SubFinalClass();
sfc.show();
}
}
3.fianl关键字修饰成员变量体现在该变量必须初始化且不能改变
主要用于防止不经意间造成改变,如:java.lang.Thread类中MAX_PRIORITY等
/**
* @auther weiwei
* @date 2021/3/30 20:57
* @description final修饰成员变量
*/
public class FinalMemberTest {
//private final int cnt = 1; // 显示初始化
private final int cnt;
/*{
cnt = 2; // 构造块中进行初始化
}*/
public FinalMemberTest(){
cnt = 3; // 构造方法体中进行初始化
}
public static void main(String[] args) {
// 声明FinalMemberTest类型的引用指向该类型的对象
FinalMemberTest fmt = new FinalMemberTest();
// 打印成员变量数值
System.out.println("fmt.cnt = " + fmt.cnt); // 0
}
}
常量
很少单独使用final关键字来修饰成员变量,通常使用public static final关键字共同修饰成员变量来表达常量的含义,常量的命名规范要求所有字母都要大写,不同单词之间采用下划线连接。
public static final double PI = 3.14;
多态和特殊类
多态(zzzz)
概念
多态指同一种事物表现出来的多种形态(父类引用指向子类对象)
语法格式
父类类型 引用变量名 = new 子类类型();
如:
Shape sr = new Rect();
sr.show();
Shape类和Rect类的实现
public class Shape {
private int x;
private int y;
public Shape() {
}
public Shape(int x, int y) {
setX(x);
setY(y);
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public void show(){
System.out.println("横坐标:" + getX() + "纵坐标:" + getY());
}
}
public class Rect extends Shape{
private int len;
private int wid;
public Rect() {
}
public Rect(int x, int y, int len, int wid) {
super(x, y);
setLen(len);
setWid(wid);
}
public int getLen() {
return len;
}
public void setLen(int len) {
if(len > 0){
this.len = len;
}else {
System.out.println("长度不合理!!");
}
}
public int getWid() {
return wid;
}
public void setWid(int wid) {
if(wid > 0){
this.wid = wid;
}else {
System.out.println("宽度不合理!!");
}
}
@Override
public void show() {
super.show();
System.out.println("长度是:" + getLen() + "宽度是:" + getWid());
}
}
public class ShapeRectTest {
public static void main(String[] args) {
// 1.声明Shape类型的引用指向Shape类型的对象并打印特征
Shape s1 = new Shape(1,2);
// 当Rect类中没有重写show方法时,下面调用Shape类中的show方法
// 当Rect类中重写show方法时,下面调用Shape类中的show方法
s1.show(); // 1 2
// 使用ctrl+d赋值当前行,alt+shift+上下方向键移动
System.out.println("------------------------------");
// 2.声明Rect类型的应用指向Rect类型的对象并打印特征
Rect r1 = new Rect(3,4,5,6);
// 当Rect类中没有重写show方法时,下面调用Shape类中的show方法
// 当Rect类中重写show方法时,下面调用Rect类中的show方法
r1.show(); // 3 4 5 6
System.out.println("------------------------------");
// 3.声明Shape类型的引用指向Rect类型的对象并打印特征
Shape sr = new Rect(7,8,9,10);
// 当Rect类中没有重写show方法时,下面调用Shape类中的show方法
// 当Rect类中重写show方法后,下面的代码在编译阶段调用Shape类的方法,在运行阶段调用Rect类中的show方法
sr.show(); // 7 8 9 10
}
}
多态的特点
1.当父类类型的引用指向子类类型的对象时,父类的引用可以直接调用父类独有的方法
2.当父类类型的引用指向子类类型的对象时,父类的引用不可以直接调用子类独有的方法
3.对于父子类都有的非静态方法来说,编译阶段调用父类版本,运行阶段调用子类重写的版本(动态绑定)
4.对于父子类都有的静态方法来说,编译和运行阶段都调用父类版本
public class Shape {
private int x;
private int y;
public Shape() {
}
public Shape(int x, int y) {
setX(x);
setY(y);
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public void show(){
System.out.println("横坐标:" + getX() + "纵坐标:" + getY());
}
// 自定义静态方法
public static void test(){
System.out.println("Shape类中的静态方法!");
}
}
public class Rect extends Shape{
private int len;
private int wid;
public Rect() {
}
public Rect(int x, int y, int len, int wid) {
super(x, y);
setLen(len);
setWid(wid);
}
public int getLen() {
return len;
}
public void setLen(int len) {
if(len > 0){
this.len = len;
}else {
System.out.println("长度不合理!!");
}
}
public int getWid() {
return wid;
}
public void setWid(int wid) {
if(wid > 0){
this.wid = wid;
}else {
System.out.println("宽度不合理!!");
}
}
@Override
public void show() {
super.show();
System.out.println("长度是:" + getLen() + "宽度是:" + getWid());
}
// 自定义静态方法
// @Override Error:历史原因,不是真正意义上的重写
public static void test(){
System.out.println("----Rect类中的静态方法!");
}
}
public class ShapeRectTest {
public static void main(String[] args) {
// 1.声明Shape类型的引用指向Shape类型的对象并打印特征
Shape s1 = new Shape(1,2);
// 当Rect类中没有重写show方法时,下面调用Shape类中的show方法
// 当Rect类中重写show方法时,下面调用Shape类中的show方法
s1.show(); // 1 2
// 使用ctrl+d赋值当前行,alt+shift+上下方向键移动
System.out.println("------------------------------");
// 2.声明Rect类型的应用指向Rect类型的对象并打印特征
Rect r1 = new Rect(3,4,5,6);
// 当Rect类中没有重写show方法时,下面调用Shape类中的show方法
// 当Rect类中重写show方法时,下面调用Rect类中的show方法
r1.show(); // 3 4 5 6
System.out.println("------------------------------");
// 3.声明Shape类型的引用指向Rect类型的对象并打印特征
Shape sr = new Rect(7,8,9,10);
// 当Rect类中没有重写show方法时,下面调用Shape类中的show方法
// 当Rect类中重写show方法后,下面的代码在编译阶段调用Shape类的方法,在运行阶段调用Rect类中的show方法
sr.show(); // 7 8 9 10
System.out.println("------------------------------");
// 4.测试Shape类型的引用能否直接调用父类和子类独有的方法呢???
int ia = sr.getY();
System.out.println("获取到的横坐标是:" + ia); // 7
//sr.getLen(); error Shape类中找不到getLen方法,也就是在Shape类中查找
// 调用静态方法
sr.test(); // 提示:不建议使用 引用. 的方式访问
Shape.test(); // 推荐使用 类名. 的方式访问
}
}
引用数据类型之间的转换
自动类型转换
主要指小类型向大类型的转换,也就是子类转为父类,也叫向上转型
强制类型转换
主要指大类型向小类型的转换,也就是父类转为子类,叫做向下转型或隐式类型转换
注意事项
引用数据类型直接的转换必须发生在父子类之间,否则编译报错
若强转的目标类型并不是该引用真正指向的数据类型时则编译通过,运行阶段发生类型转换异常
为避免上述错误的发生,应该在强转之前进行判断,格式如下:
if(引用变量 instanceof 数据类型)
判断引用变量指向的对象是否为后面的数据类型,是则为true,否则为false
public class Shape {
private int x;
private int y;
public Shape() {
}
public Shape(int x, int y) {
setX(x);
setY(y);
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public void show(){
System.out.println("横坐标:" + getX() + "纵坐标:" + getY());
}
// 自定义静态方法
public static void test(){
System.out.println("Shape类中的静态方法!");
}
}
public class Rect extends Shape{
private int len;
private int wid;
public Rect() {
}
public Rect(int x, int y, int len, int wid) {
super(x, y);
setLen(len);
setWid(wid);
}
public int getLen() {
return len;
}
public void setLen(int len) {
if(len > 0){
this.len = len;
}else {
System.out.println("长度不合理!!");
}
}
public int getWid() {
return wid;
}
public void setWid(int wid) {
if(wid > 0){
this.wid = wid;
}else {
System.out.println("宽度不合理!!");
}
}
@Override
public void show() {
super.show();
System.out.println("长度是:" + getLen() + "宽度是:" + getWid());
}
// 自定义静态方法
// @Override Error:历史原因,不是真正意义上的重写
public static void test(){
System.out.println("----Rect类中的静态方法!");
}
}
public class Circle extends Shape{
}
public class ShapeRectTest {
public static void main(String[] args) {
// 1.声明Shape类型的引用指向Shape类型的对象并打印特征
Shape s1 = new Shape(1,2);
// 当Rect类中没有重写show方法时,下面调用Shape类中的show方法
// 当Rect类中重写show方法时,下面调用Shape类中的show方法
s1.show(); // 1 2
// 使用ctrl+d赋值当前行,alt+shift+上下方向键移动
System.out.println("------------------------------");
// 2.声明Rect类型的应用指向Rect类型的对象并打印特征
Rect r1 = new Rect(3,4,5,6);
// 当Rect类中没有重写show方法时,下面调用Shape类中的show方法
// 当Rect类中重写show方法时,下面调用Rect类中的show方法
r1.show(); // 3 4 5 6
System.out.println("------------------------------");
// 3.声明Shape类型的引用指向Rect类型的对象并打印特征
// 相当于从Rect类型到Shape类型的转换 也就是子类到父类的转换 小到大的转换 自动类型转换
Shape sr = new Rect(7,8,9,10);
// 当Rect类中没有重写show方法时,下面调用Shape类中的show方法
// 当Rect类中重写show方法后,下面的代码在编译阶段调用Shape类的方法,在运行阶段调用Rect类中的show方法
sr.show(); // 7 8 9 10
System.out.println("------------------------------");
// 4.测试Shape类型的引用能否直接调用父类和子类独有的方法呢???
int ia = sr.getY();
System.out.println("获取到的横坐标是:" + ia); // 7
//sr.getLen(); error Shape类中找不到getLen方法,也就是在Shape类中查找
// 调用静态方法
sr.test(); // 提示:不建议使用 引用. 的方式访问
Shape.test(); // 推荐使用 类名. 的方式访问
System.out.println("------------------------------");
// 5.使用父类类型的引用调用子类独有方法的方式
// 相当于Shape类型到Rect类型的转换,也就是父类到子类的转换 大到小的转换 强制类型转换
int ib = ((Rect) sr).getLen();
System.out.println("获取到的长度是:" + ib);
// 希望将Shape类型转换为String类型 强制类型转换要求必须拥有父子类关系
//String str1 = (String)sr; Error
// 希望将Shape类型转换为Circle类型,下面没有报错
//Circle c1 = (Circle) sr; // 编译OK,但运行阶段发生ClassCastException类型转换异常
// 在强制类型转换之前应该使用instanceof进行类型的判断
// 判断sr指向堆区内存中的对象是否为Circle类型,如果是则返回true,否则返回false
if(sr instanceof Circle){
System.out.println("可以放心的转换啦!");
Circle c1 = (Circle)sr;
}else{
System.out.println("强转有风险,操作需谨慎!");
}
}
}
多态的实际意义
屏蔽了不同子类的差异性实现通用的编程带来不同的效果
public class Shape {
private int x;
private int y;
public Shape() {
}
public Shape(int x, int y) {
setX(x);
setY(y);
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public void show(){
System.out.println("横坐标:" + getX() + "纵坐标:" + getY());
}
// 自定义静态方法
public static void test(){
System.out.println("Shape类中的静态方法!");
}
}
public class Rect extends Shape{
private int len;
private int wid;
public Rect() {
}
public Rect(int x, int y, int len, int wid) {
super(x, y);
setLen(len);
setWid(wid);
}
public int getLen() {
return len;
}
public void setLen(int len) {
if(len > 0){
this.len = len;
}else {
System.out.println("长度不合理!!");
}
}
public int getWid() {
return wid;
}
public void setWid(int wid) {
if(wid > 0){
this.wid = wid;
}else {
System.out.println("宽度不合理!!");
}
}
@Override
public void show() {
super.show();
System.out.println("长度是:" + getLen() + "宽度是:" + getWid());
}
// 自定义静态方法
// @Override Error:历史原因,不是真正意义上的重写
public static void test(){
System.out.println("----Rect类中的静态方法!");
}
}
public class Circle extends Shape{
private int ir;
public Circle() {
}
public Circle(int x, int y, int ir) {
super(x, y);
setIr(ir);
}
public int getIr() {
return ir;
}
public void setIr(int ir) {
if(ir > 0){
this.ir = ir;
}else {
System.out.println("半径不合理!");
}
}
@Override
public void show() {
super.show();
System.out.println("半径是: " + getIr());
}
}
public class ShapeTest {
// 自定义成员方法实现将参数指定矩形对象特征打印出来的行为,也就是绘制图形的行为
// public static void draw(Rect r){
// r.show(); // 1 2 3 4
// }
// 自定义成员方法实现将参数指定圆形对象特征打印出来的行为
// public static void draw(Circle c){
// c.show();
// }
// 自定义成员方法实现既能打印矩形对象特征又能打印圆形对象特征,对象有参数传入 子类 is a 父类
// Shape s = new Rect(1, 2, 3, 4); 父类类型的引用指向子类类型的对象,形成了多态
// Shape s = new Circle(5,6,7); 多态
// 多态使用场合一:通过参数传递形成了多态
public static void draw(Shape s){
// 编译阶段调用父类的版本,运行阶段调用子类重写以后的版本
s.show();
}
public static void main(String[] args) {
// Rect r = new Rect(1,2,3,4);
// r.show();
ShapeTest.draw(new Rect(1,2,3,4));
ShapeTest.draw(new Circle(5,6,7));
}
}
抽象(重点)
抽象方法概念
主要指不能具体实现的方法并且使用abstract关键字修饰,也就是没有方法体
具体格式如下:
访问权限 abstract 返回值类型 方法名(形参列表);
public abstract void cry();
抽象类概念
主要指不能具体实例化的类并且使用abstract关键字修饰,也就是不能创建对象
抽象类和抽象方法的关系
1.抽象类中可以有成员变量、构造方法、成员方法
2.抽象类中可以没有抽象方法,也可以有抽象方法
3.用于抽象方法的类必须是抽象类,因此真正意义上的抽象类应该是具有抽象方法并且使用abstract关键字修饰的类
抽象类的实际意义
1.不在于创建对象而在于被继承
2.当一个类继承抽象类后必须重写抽象方法,否则该类也变成抽象类,也就是抽象类的子类具有强制性和规范性,因此叫做模板设计模式
public abstract class AbstractTest {
private int cnt;
public AbstractTest() {
}
public AbstractTest(int cnt) {
setCnt(cnt);
}
public int getCnt() {
return cnt;
}
public void setCnt(int cnt) {
this.cnt = cnt;
}
// 自定义抽象方法
public abstract void show();
public static void main(String[] args) {
// 声明该类类型的引用指向该类类型的对象
//AbstractTest at = new AbstractTest();
//System.out.println("at.cnt = " + at.cnt); // 0
}
}
public class SubAbstractTest extends AbstractTest{
@Override
public void show() {
System.out.println("其实我是被迫重写的,否则我也得变成抽象的呀!");
}
public static void main(String[] args) {
// 1.声明本类类型的引用指向本类类型的对象
SubAbstractTest sat = new SubAbstractTest();
sat.show();
}
}
开发者经验
推荐使用多态的格式,此时父类引用直接调用的所有方法一定是父类中拥有的方法,若以后更换子类,只需要将new关键字后面的子类类型修改而其他地方无须改变就可以立即生效,从而提高代码的可维护性和可扩展性。
该方式缺点:父类引用不能直接调用子类独有的方法,若调用则需要强制类型转换。
public abstract class AbstractTest {
private int cnt;
public AbstractTest() {
}
public AbstractTest(int cnt) {
setCnt(cnt);
}
public int getCnt() {
return cnt;
}
public void setCnt(int cnt) {
this.cnt = cnt;
}
// 自定义抽象方法
public abstract void show();
public static void main(String[] args) {
// 声明该类类型的引用指向该类类型的对象
//AbstractTest at = new AbstractTest();
//System.out.println("at.cnt = " + at.cnt); // 0
}
}
public class SubAbstractTest extends AbstractTest{
@Override
public void show() {
System.out.println("其实我是被迫重写的,否则我也得变成抽象的呀!");
}
public static void main(String[] args) {
// 1.声明本类类型的引用指向本类类型的对象
SubAbstractTest sat = new SubAbstractTest();
sat.show();
System.out.println("-----------------------------------");
// 2.声明AbstractTest类型的引用指向子类的对象,形成了多态
// 多态的使用场合之二:直接在方法体中使用抽象类的引用指向子类类型的对象
AbstractTest at = new SubAbstractTest2();
// 编译阶段调用父类版本,运行阶段调用子类版本
at.show();
((SubAbstractTest2) at).test();
System.out.println("-----------------------------------");
SubAbstractTest2 sat2 = new SubAbstractTest2();
sat2.test();
}
}
public class SubAbstractTest2 extends AbstractTest{
@Override
public void show() {
System.out.println("使用多态方式可以提高代码的可维护性哦!");
}
public void test(){
System.out.println("第二个子类中独有的方法!");
}
}
抽象类的应用
public abstract class Account {
private double money;
public Account() {
}
public Account(double money) {
setMoney(money);
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
if(money > 0){
this.money = money;
}else {
System.out.println("金额不合理!!");
}
}
// 自定义抽象方法实现计算利息并返回的功能描述
public abstract double getLixi();
}
public class FixedAccount extends Account {
public FixedAccount(int i) {
super(i); // 表示调用父类的有参构造方法
}
@Override
public double getLixi() {
// 利息 = 本金 * 利率 * 时间
return getMoney() * 0.03 * 1;
}
public static void main(String[] args) {
// 1.声明一个Account类型的引用指向子类类型的对象,形成了多态
Account acc = new FixedAccount(1000);
double res = acc.getLixi();
System.out.println("计算的利息是: " + res); // 30.0
}
}
笔试考点
// 自定义抽象方法实现计算利息并返回的功能描述
public abstract double getLixi();
// private 和 abstract关键字不能共同修饰一个方法
//private abstract double getLixi();
// final 和 abstract关键字不能共同修饰一个方法
//public final abstract double getLixi();
//static 和 abstract关键字不能共同修饰一个方法
//public static abstract double getLixi();
接口(重点)
概念
接口就一种比抽象类还抽象的类,体现在所有方法都为抽象方法
定义类的关键字是class,而定义接口的关键字是interface
public interface InterfaceTest {
/*public static final*/ int CNT = 1; // 里面只能有常量
//private void show(){} // 从Java9开始允许接口中出现私有方法
/*public abstract*/ void show(); // 里面只能有抽象方法(新特性除外),注释中的关键字可以省略,但建议写上
}
接口的意义
public interface Metal {
// 自定义抽象方法描述发光的行为
public abstract void shine();
}
public interface Money {
// 自定义抽象方法描述购物行为
public abstract void buy();
}
public class Gold implements Metal,Money{
@Override
public void shine() {
System.out.println("发出了金黄色的光芒...");
}
@Override
public void buy() {
System.out.println("买了好多好吃的...");
}
public static void main(String[] args) {
// 1.声明接口类型的引用指向实现类的对象,形成了多态
Metal mt = new Gold();
mt.shine();
Money mn = new Gold();
mn.buy();
}
}
类和接口之间的关系
名称 | 关键字 | 关系 |
类和类 | 使用extends表达继承关系 | 支持单继承 |
类和接口 | 使用implements表达实现关系 | 支持多实现 |
接口和接口 | 使用extends表达继承关系 | 支持多继承 |
public interface Runner {
// 自定义抽象方法描述奔跑行为
public abstract void run();
}
//接口只能继承接口,不能继承普通类
public interface Hunter extends Runner{
// 自定义抽象方法描述捕猎行为
public abstract void hunt();
}
public class Man implements Hunter{
@Override
public void hunt() {
System.out.println("正在追赶一只小白兔!");
}
@Override
public void run() {
System.out.println("兔子正在被一只大熊追赶,玩命奔跑中!");
}
public static void main(String[] args) {
// 1.声明接口类型的引用指向实现类的对象,形成了多态
Runner runner = new Man();
runner.run();
Hunter hunter = new Man();
hunter.hunt();
}
}
接口和抽象类的区别
1.定义抽象类的关键字是abstract class,而定义接口的关键字是interface
2.继承抽象类的关键字是extends,而实现接口的关键字是implements
3.继承抽象类支持单继承,而实现接口支持多实现
4.抽象类中可以有构造方法,而接口中不可以有构造方法
5.抽象类中可以有成员变量,而接口中只可以有常量
6.抽象类中可以有成员方法,而接口中只可以有抽象方法
7.抽象类中增加方法时子类可以不用重写,而接口中增加方法时实现类需要重写(Java8以前的版本)
8.从Java8开始增加新特性,接口中允许出现非抽象方法和静态方法,但非抽象方法需要使用default关键字修饰
9.从Java9开始增加新特性,接口中允许出现私有方法
public interface Runner {
// 自定义抽象方法描述奔跑行为
public abstract void run();
}
//接口只能继承接口,不能继承普通类
public interface Hunter extends Runner{
// 自定义抽象方法描述捕猎行为
public abstract void hunt();
// 将两个默认方法中重复的代码提取出来打包成一个方法在下面的两个方法中分别调用即可
private void show(){
System.out.println("在以后的开发中尽量减少重复的代码,也就是减少代码的冗余!");
}
// 增加一个方法
//public abstract void show1();
// 增加非抽象方法
public default void show1(){
//System.out.println("在以后的开发中尽量减少重复的代码,也就是减少代码的冗余!");
System.out.println("show1方法中:这里仅仅是接口中默认功能,实现类可以自由选择是否重写!");
}
// 增加非抽象方法
public default void show2(){
//System.out.println("在以后的开发中尽量减少重复的代码,也就是减少代码的冗余!");
System.out.println("show2方法中:这里仅仅是接口中默认功能,实现类可以自由选择是否重写!");
}
// 增加静态方法 隶属于类层级,也就是接口层级
public static void test(){
System.out.println("这是静态方法,可以通过 接口名. 的方式的调用,省略对象的创建");
}
}
public class Man implements Hunter{
@Override
public void hunt() {
System.out.println("正在追赶一只小白兔!");
}
@Override
public void run() {
System.out.println("兔子正在被一只大熊追赶,玩命奔跑中!");
}
@Override
public void show1() {
System.out.println("为了给你几分薄面,我决定重写一下!");
}
public static void main(String[] args) {
// 1.声明接口类型的引用指向实现类的对象,形成了多态
Runner runner = new Man();
runner.run();
Hunter hunter = new Man();
hunter.hunt();
System.out.println("-----------------------");
// 2.可以使用 接口名称. 的方式调用接口中的静态方法
Hunter.test();
}
}
特殊类
内部类
概念
当一个类的定义出现在另外一个类的类体中时,那么这个类叫做内部类(Inner),而这个内部类所在的类叫做外部类(Outer)
类中的内容:成员变量、成员方法、构造方法、静态成员、构造块和静态代码块、内部类
分类
普通内部类----直接将一个类的定义放在另外一个类的类体中
静态内部类----使用static关键字修饰的内部类,隶属于类层级
局部内部类----直接讲一个类的定义放在方法体的内部
匿名内部类----指没有名字的内部类
普通(成员)内部类
格式
访问修饰符 class 外部类的类名{
访问修饰符 class 内部类的类名{
内部类的类体;
}
}
/**
* @auther weiwei
* @date 2021/3/31 19:25
* @description 编程实现普通内部类的蒂尼和使用
*/
public class NormalOuter {
private int cnt = 1;
// 定义普通内部类,隶属于外部类的成员,并且是对象层级
public class NormalInner{
private int ia = 2;
public NormalInner(){
System.out.println("普通内部类的构造方法体执行到了!");
}
public void show(){
System.out.println("外部类中的变量cnt的数值为: " + cnt); // 1
System.out.println("ia = " + ia); // 2
}
}
}
public class NormalOuterTest {
public static void main(String[] args) {
// 1.声明NormalOuter类型的引用指向该类型的
NormalOuter no = new NormalOuter();
// 2.声明NormalOuter类中内部类的引用指向内部类的对象
NormalOuter.NormalInner ni = no.new NormalInner();
// 调用内部类中的show方法
ni.show();
}
}
使用方式
1.普通内部类和普通类一样可以定义成员变量、成员方法以及构造方法
2.普通内部类和普通类一样可以使用final或abstract关键字修饰
3.普通内部类还可以使用private或protected关键字进行修饰
4.普通内部类需要使用外部类对象来创建对象
5.如果内部了访问外部类中与本类内部类同名的成员本类或方法时,需要使用this关键字
/**
* @auther weiwei
* @date 2021/3/31 19:25
* @description 编程实现普通内部类的定义和使用
*/
public class NormalOuter {
private int cnt = 1;
// 定义普通内部类,隶属于外部类的成员,并且是对象层级
/*private*/ public /*final*/ class NormalInner{
private int ia = 2;
private int cnt = 3;
public NormalInner(){
System.out.println("普通内部类的构造方法体执行到了!");
}
public void show(){
System.out.println("外部类中的变量cnt的数值为: " + cnt); // 1
System.out.println("ia = " + ia); // 2
}
public void show2(int cnt){
System.out.println("形参变量cnt = " + cnt); //局部优先原则 4
System.out.println("内部类中cnt = " + this.cnt); // 3
System.out.println("外部类中cnt = " + NormalOuter.this.cnt); // 1
}
}
}
public class NormalOuterTest {
public static void main(String[] args) {
// 1.声明NormalOuter类型的引用指向该类型的
NormalOuter no = new NormalOuter();
// 2.声明NormalOuter类中内部类的引用指向内部类的对象
NormalOuter.NormalInner ni = no.new NormalInner();
// 调用内部类中的show方法
ni.show();
System.out.println("------------------------------");
ni.show2(4);
}
}
静态内部类
格式
访问修饰符 class 外部类的类名{
访问修饰符 static class 内部类的类名{
内部类的类体;
}
}
使用方式
1.静态内部类不能直接访问外部类的非静态成员
2.静态内部类可以直接创建对象
3.如果静态内部类访问外部类与本类同名的成员方法或变量时,需要使用 类名. 的方式访问
/**
* @auther weiwei
* @date 2021/4/1 19:13
* @description 静态内部类的定义和使用
*/
public class StaticOuter {
private int cnt = 1; // 隶属于对象层级
private static int snt = 2; // 隶属于类层级
public /*static*/ void show(){
System.out.println("外部类中的show方法就是这里!");
}
/**
* 定义静态内部类 由static修饰隶属于类层级
*/
public static class StaticInner{
private int ia = 3;
private static int snt = 4;
public StaticInner(){
System.out.println("静态内部类的构造方法哦!");
}
public void show(){
System.out.println("ia = " + ia); // 3
System.out.println("外部类中的snt = " + snt); // 2
//System.out.println("外部类中的snt = " + cnt); // Error:静态上下文中不能访问非静态的成员,此时可能还没有创建对象
}
public void show2(int snt){
System.out.println("snt = " + snt); //就近原则
System.out.println("内部类中snt = " + StaticInner.snt); // 4
System.out.println("外部类中snt = " + StaticOuter.snt); // 2
// StaticOuter.show();
new StaticOuter().show();
}
}
}
public class StaticOuterTest {
public static void main(String[] args) {
// 1.声明StaticInner类型的引用指向该类型的对象
StaticOuter.StaticInner si = new StaticOuter.StaticInner();
// 2.调用show方法进行测试
si.show();
System.out.println("--------------------------------------");
si.show2(5);
}
}
局部(方法)内部类
格式
访问修饰符 class 外部类的类名{
访问修饰符 返回值类型 成员方法名(形参列表){
class 内部类的类名{
内部类的类体;
}
}
内部类的类体; }
}
使用方式
1.局部内部类只能在该方法内部使用
2.局部内部类可以在方法体内部直接创建对象
33.局部内部类不能使用访问控制符static关键字修饰
4.局部内部类可以使用外部方法的局部变量,但是必须是final的。由局部内部类和局部变量的声明周期不同所致
/**
* @auther weiwei
* @date 2021/4/1 19:37
* @description 编程实现局部内部类的定义和使用
*/
public class AreaOuter {
private int cnt = 1;
public void show(){
// 定义一个局部变量进行测试,从Java8开始默认理解为final修饰的内容
// 虽然可以省略final关键字,但是建议加上
final int ic = 4;
// 定义局部内部类,只在当前方法体的内部好使
class AreaInner{
private int ia = 2;
public AreaInner(){
System.out.println("局部内部类的构造方法!");
}
public void test(){
System.out.println("ia = " + ia); // 2
System.out.println("cnt = " + cnt); // 1
//ic = 5; Error
System.out.println("ic = " + ic); // 4
}
}
// 声明局部内部类的引用指向局部内部类的对象
AreaInner ai = new AreaInner();
ai.test();
}
}
public class AreaInnerTest {
public static void main(String[] args) {
// 1.声明外部类的引用指向外部类的对象
AreaOuter ao = new AreaOuter();
// 2.通过show方法的调用实现局部内部类的定义和使用
ao.show();
}
}
回调模式(重点掌握)
回调模式是指----如果一个方法的参数是接口类型,则在调用该方法时,需要创建并传递一个实现此接口类型的对象;而该方法在运行时会调用到参数对象中所实现的方法(接口中定义的)。
public interface AnonymousInterface {
// 自定义抽象方法
public abstract void show();
}
public class AnonymousInterfaceImpl implements AnonymousInterface{
@Override
public void show() {
System.out.println("这里是接口的实现类!");
}
}
public class AnonymousInterfaceTest {
// 假设已有下面的方法,请问如何调用下面的方法
// AnonymousInterface ai = new AnonymousInterfaceImpl();
// 接口类型的引用指向实现类的对象,形成了多态
public static void test(AnonymousInterface ai){
// 编译阶段调用父类版本,运行调用子类重写的版本
ai.show();
}
public static void main(String[] args) {
//AnonymousInterfaceTest.test(new AnonymousInterface()); // Error:接口不能实例化
AnonymousInterfaceTest.test(new AnonymousInterfaceImpl());
}
}
匿名内部类(重点掌握)
当接口/类类型的引用作为方法的形参时,实参的传递方式有两种:
1.自定义类实现接口/继承类并重写方法,然后创建该类对象作为实参传递
2.使用上述匿名内部类的语法格式得到接口/类类型的引用即可
匿名内部类语法格式(重点)
接口/父类类型 引用变量名 = new 接口/父类类型(){方法的重写};
public interface AnonymousInterface {
// 自定义抽象方法
public abstract void show();
}
public class AnonymousInterfaceImpl implements AnonymousInterface{
@Override
public void show() {
System.out.println("这里是接口的实现类!");
}
}
public class AnonymousInterfaceTest {
// 假设已有下面的方法,请问如何调用下面的方法
// AnonymousInterface ai = new AnonymousInterfaceImpl();
// 接口类型的引用指向实现类的对象,形成了多态
public static void test(AnonymousInterface ai){
// 编译阶段调用父类版本,运行调用子类重写的版本
ai.show();
}
public static void main(String[] args) {
//AnonymousInterfaceTest.test(new AnonymousInterface()); // Error:接口不能实例化
AnonymousInterfaceTest.test(new AnonymousInterfaceImpl());
System.out.println("----------------------------------------------------------");
// 使用匿名内部类的语法格式来得到接口类型的引用,格式为:接口/父类类型 引用变量名 = new 接口/父类类型(){方法的重写};
AnonymousInterface ait = new AnonymousInterface() {
@Override
public void show() {
System.out.println("匿名内部类就是这么玩的,虽然你很抽象!");
}
};
// 从Java8开始提出新特性lamda表达式可以简化上述代码,格式为:(参数列表) -> {方法体}
AnonymousInterface ait2 = ()-> System.out.println("lamda表达式原来是如此简单!");
AnonymousInterfaceTest.test(ait2);
}
}
枚举类
枚举的定义
1.使用public static final表示常量描述较为繁琐,使用enum关键字来定义枚举类型取代常量,枚举类型是从Java5开始增加的一种引用数据类型
2.枚举值就是当前类的类型,也就是指向本类的对象,默认使用public static final关键字共同修饰,因此采用 枚举类型. 的方式调用
3.枚举类型可以自定义构造方法,但是构造方法的修饰符必须是private,默认也是私有的
/**
* @auther weiwei
* @date 2021/4/1 20:36
* @description 编程实现所有方向的枚举,上、下、左、右
*/
public class Direction {
private final String desc; // 用于描述方向字符串的成员变量
// 2.声明本类类型的引用指向本类类型的对象
public static final Direction UP = new Direction("向上");
public static final Direction DOWN = new Direction("向下");
public static final Direction LEFT = new Direction("向左");
public static final Direction RIGHT = new Direction("向右");
// 通过构造方法实现成员变量的初始化,更加灵活
// 私有化构造方法,此时该构造方法只能在本类的内部使用
private Direction(String desc){
this.desc = desc;
}
// 通过公有的get方法可以在本类的外部访问该类成员变量的数值
public String getDesc() {
return desc;
}
}
/**
* @auther weiwei
* @date 2021/4/1 20:36
* @description 编程实现所有方向的枚举,上、下、左、右 枚举类型要求所有枚举值必须放在枚举类型的最前面
*/
public enum DirectionEnum {
// 2.声明本类类型的引用指向本类类型的对象
UP("向上"),DOWN("向下"),LEFT("向左"),RIGHT("向右");
private final String desc; // 用于描述方向字符串的成员变量
// 通过构造方法实现成员变量的初始化,更加灵活
// 私有化构造方法,此时该构造方法只能在本类的内部使用
private DirectionEnum(String desc){
this.desc = desc;
}
// 通过公有的get方法可以在本类的外部访问该类成员变量的数值
public String getDesc() {
return desc;
}
}
public class DirectionTest {
public static void main(String[] args) {
/*// 1.声明Direction类型的引用指向该类型的对象并打印特征
Direction d1 = new Direction("向上");
System.out.println("获取到的字符串时: " + d1.getDesc()); //向上
Direction d2 = new Direction("向下");
System.out.println("获取到的字符串时: " + d2.getDesc()); //向下
Direction d3 = new Direction("向左");
System.out.println("获取到的字符串时: " + d3.getDesc()); //向左
Direction d4 = new Direction("向右");
System.out.println("获取到的字符串时: " + d4.getDesc()); //向右
System.out.println("-------------------------------------------");
Direction d5 = new Direction("向前");
System.out.println("获取到的字符串时: " + d5.getDesc()); //向前*/
//Direction.UP = 2; Error:类型不匹配
//Direction d2 = null;
//Direction.UP = d2; Error:final关键字修饰
Direction d1 = Direction.UP;
System.out.println("获取到的方向是: " + d1.getDesc());
System.out.println("-------------------------------------------");
// 使用一下Java5开始的枚举类型
DirectionEnum de = DirectionEnum.DOWN;
System.out.println("获取到的方向为:" + de.getDesc()); // 向下
}
}
自定义类和枚举类在switch结构的使用
/**
* @auther weiwei
* @date 2021/4/1 20:36
* @description 编程实现所有方向的枚举,上、下、左、右
*/
public class Direction {
private final String desc; // 用于描述方向字符串的成员变量
// 2.声明本类类型的引用指向本类类型的对象
public static final Direction UP = new Direction("向上");
public static final Direction DOWN = new Direction("向下");
public static final Direction LEFT = new Direction("向左");
public static final Direction RIGHT = new Direction("向右");
// 通过构造方法实现成员变量的初始化,更加灵活
// 私有化构造方法,此时该构造方法只能在本类的内部使用
private Direction(String desc){
this.desc = desc;
}
// 通过公有的get方法可以在本类的外部访问该类成员变量的数值
public String getDesc() {
return desc;
}
}
/**
* @auther weiwei
* @date 2021/4/1 20:36
* @description 编程实现所有方向的枚举,上、下、左、右 枚举类型要求所有枚举值必须放在枚举类型的最前面
*/
public enum DirectionEnum {
// 2.声明本类类型的引用指向本类类型的对象
UP("向上"),DOWN("向下"),LEFT("向左"),RIGHT("向右");
private final String desc; // 用于描述方向字符串的成员变量
// 通过构造方法实现成员变量的初始化,更加灵活
// 私有化构造方法,此时该构造方法只能在本类的内部使用
private DirectionEnum(String desc){
this.desc = desc;
}
// 通过公有的get方法可以在本类的外部访问该类成员变量的数值
public String getDesc() {
return desc;
}
}
public class DirectionUseTest {
// 自定义静态方法实现根据参数指定的字符串内容来打印具体方向的信息
public static void test1(String str){
switch(str){
case "向上":
System.out.println("抬头望明月!");
break;
case "向下":
System.out.println("低头思故乡!");
break;
case "向左":
System.out.println("左前黄");
break;
case "向右":
System.out.println("右擎苍");
default:
System.out.println("没有这样的方法!");
}
}
// 自定义静态方法实现根据参数指定的字符串内容来打印具体方向的信息
public static void test2(DirectionEnum de){
switch(de){
case UP:
System.out.println("抬头望明月!");
break;
case DOWN:
System.out.println("低头思故乡!");
break;
case LEFT:
System.out.println("左前黄");
break;
case RIGHT:
System.out.println("右擎苍");
default:
System.out.println("没有这样的方法!");
}
}
public static void main(String[] args) {
DirectionUseTest.test1(Direction.UP.getDesc());
DirectionUseTest.test1("今天是个好日子!");
System.out.println("-------------------------------");
DirectionUseTest.test2(DirectionEnum.DOWN);
//DirectionUseTest.test2("今天是个好日子!"); Error:类型不匹配,减少了出错的可能性
}
}
Enum类
所有枚举类都继承自java.lang.Enum类,常用方法如下:
static T[] values() | 返回当前枚举类中的所有对象 |
String toString() | 返回当前枚举类对象的名称 |
int ordinal() | 获取枚举对象在枚举类中的索引位置 |
static T valueOf(String str) | 将参数指定的字符串名转为当前枚举类的对象 |
int compareTo(E o) | 比较两个枚举对象在定义时的顺序 |
/**
* @auther weiwei
* @date 2021/4/1 20:36
* @description 编程实现所有方向的枚举,上、下、左、右 枚举类型要求所有枚举值必须放在枚举类型的最前面
*/
public enum DirectionEnum {
// 2.声明本类类型的引用指向本类类型的对象
UP("向上"),DOWN("向下"),LEFT("向左"),RIGHT("向右");
private final String desc; // 用于描述方向字符串的成员变量
// 通过构造方法实现成员变量的初始化,更加灵活
// 私有化构造方法,此时该构造方法只能在本类的内部使用
private DirectionEnum(String desc){
this.desc = desc;
}
// 通过公有的get方法可以在本类的外部访问该类成员变量的数值
public String getDesc() {
return desc;
}
}
/**
* @auther weiwei
* @date 2021/4/1 21:35
* @description 编程实现方向枚举类的测试,调用从Enum类中继承下来的方法
*/
public class DirectionEnumTest {
public static void main(String[] args) {
// 1.获取DirectionEnum类型中所有枚举对象
DirectionEnum[] arr = DirectionEnum.values();
// 2.打印每个枚举对象在枚举类型中的名称和索引位置
for (int i = 0; i < arr.length; i++) {
System.out.println("获取到的枚举对象名称是:" + arr[i].toString());
System.out.println("获取到的枚举对象对应的索引位置是:" + arr[i].ordinal()); //和数组一样下标从0开始
}
System.out.println("---------------------------------------");
// 3.根据参数指定的字符串得到枚举类型的对象,也就是将字符串转换为对象
//DirectionEnum de = DirectionEnum.valueOf("向下"); // 编译OK,运行发生IllegalArgumentException非法参数异常
DirectionEnum de = DirectionEnum.valueOf("DOWN"); // 编译OK,运行发生IllegalArgumentException非法参数异常
//DirectionEnum de = DirectionEnum.valueOf("UP LEFT"); // 要求字符串名称必须在枚举对象中存在
//System.out.println("转换出来的枚举对象名称是:" + de.toString());
System.out.println("转换出来的枚举对象名称是:" + de); // 当打印引用变量时,会自动调用toString方法
System.out.println("---------------------------------------");
// 4.使用获取到的枚举对象与枚举类型中已有的对象比较先后顺序
for (int i = 0; i < arr.length; i++) {
// 当调用对象在参数对象之后时,获取到的比较结果是 正数
// 当调用对象在参数对象相同位置时,获取到的比较结果是 0
// 当调用对象在参数对象之前时,获取到的比较结果是 负数
System.out.println("调用对象与数组中对象比较的先后顺序结果是:" + de.compareTo(arr[i]));
}
}
}
枚举类实现接口的方式
枚举类实现接口后需要重写抽象方法,而重写方法的方式有两种:重写一个,或者每个对象都重写
public interface DirectionInterface {
// 自定义抽象方法
public abstract void show();
}
/**
* @auther weiwei
* @date 2021/4/1 20:36
* @description 编程实现所有方向的枚举,上、下、左、右 枚举类型要求所有枚举值必须放在枚举类型的最前面
*/
public enum DirectionEnum implements DirectionInterface{
// 2.声明本类类型的引用指向本类类型的对象
// 匿名内部类的语法格式:接口/父类类型 引用变量名 = new 接口/父类类型(){ 方法的重写 };
// public static final Direction UP = new Direction("向上") { 方法的重写 };
UP("向上"){
@Override
public void show() {
System.out.println("贪吃蛇向上移动了一下!");
}
},DOWN("向下") {
@Override
public void show() {
System.out.println("贪吃蛇向下移动了一下!");
}
},LEFT("向左") {
@Override
public void show() {
System.out.println("左移了一下!");
}
},RIGHT("向右") {
@Override
public void show() {
System.out.println("右移了一下!");
}
};
private final String desc; // 用于描述方向字符串的成员变量
// 通过构造方法实现成员变量的初始化,更加灵活
// 私有化构造方法,此时该构造方法只能在本类的内部使用
private DirectionEnum(String desc){
this.desc = desc;
}
// 通过公有的get方法可以在本类的外部访问该类成员变量的数值
public String getDesc() {
return desc;
}
// 整个枚举类型只重写一次,所有对象调用同一个
/*@Override
public void show() {
System.out.println("现在可以实现接口中抽象方法的重写了!");
}*/
}
/**
* @auther weiwei
* @date 2021/4/1 21:35
* @description 编程实现方向枚举类的测试,调用从Enum类中继承下来的方法
*/
public class DirectionEnumTest {
public static void main(String[] args) {
// 1.获取DirectionEnum类型中所有枚举对象
DirectionEnum[] arr = DirectionEnum.values();
// 2.打印每个枚举对象在枚举类型中的名称和索引位置
for (int i = 0; i < arr.length; i++) {
System.out.println("获取到的枚举对象名称是:" + arr[i].toString());
System.out.println("获取到的枚举对象对应的索引位置是:" + arr[i].ordinal()); //和数组一样下标从0开始
}
System.out.println("---------------------------------------");
// 3.根据参数指定的字符串得到枚举类型的对象,也就是将字符串转换为对象
//DirectionEnum de = DirectionEnum.valueOf("向下"); // 编译OK,运行发生IllegalArgumentException非法参数异常
DirectionEnum de = DirectionEnum.valueOf("DOWN"); // 编译OK,运行发生IllegalArgumentException非法参数异常
//DirectionEnum de = DirectionEnum.valueOf("UP LEFT"); // 要求字符串名称必须在枚举对象中存在
//System.out.println("转换出来的枚举对象名称是:" + de.toString());
System.out.println("转换出来的枚举对象名称是:" + de); // 当打印引用变量时,会自动调用toString方法
System.out.println("---------------------------------------");
// 4.使用获取到的枚举对象与枚举类型中已有的对象比较先后顺序
for (int i = 0; i < arr.length; i++) {
// 当调用对象在参数对象之后时,获取到的比较结果是 正数
// 当调用对象在参数对象相同位置时,获取到的比较结果是 0
// 当调用对象在参数对象之前时,获取到的比较结果是 负数
System.out.println("调用对象与数组中对象比较的先后顺序结果是:" + de.compareTo(arr[i]));
}
System.out.println("---------------------------------------");
// 5.使用数组中每个DirectionEnum对象都去调用show方法测试
for (int i = 0; i < arr.length; i++) {
arr[i].show();
}
}
}
注解(理解)
概念
注解(Annotation)又叫标注,是从Java5开始增加的一种引用数据类型
本质上就是代码中的特殊标记,通过这些标记可以在编译、类加载、以及运行时执行指定的处理
定义和使用
定义
访问修饰符 @interface 注解名称{
注解成员;
}
自定义注解自动继承自java.lang.annotatoin.Annotation接口
通过@注解名称的方式可以修饰包、类、成员方法、成员变量、构造方法、参数、局部变量的声明等
使用
1.注解中只有成员变量没有成员方法,而注解的成员本类以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型
2.如果注解只有一个参数成员,建议使用参数名为value,而类型只能是八种基本数据类型、String类型、Class类型、enum类型以及Annotation类型
// 若一个注解中没有任何成员,则这样的注解叫做标记注解/标识注解
public @interface MyAnnotation {
//public String value(); //声明一个String类型的成员本类,名字为value 类型有要求
public String value() default "123"; //声明一个String类型的成员本类,名字为value
public String value2();
}
// 表示将标签MyAnnotation贴在Person类的代码中,使用注解时采用 成员参数名 = 成员参数值, ...
//@MyAnnotation(value = "hello",value2 = "hello world")
@MyAnnotation(value2 = "world")
public class Person {
private String name;
private int age;
}
元注解和@Retention的使用
元注解概念
1.元注解是可以注解到注解上的注解,或者说元注解是一种基本注解,但是它能够应用到其他注解上面
2.元注解主要有@Retention、@Documented、@Target、@Inherited、@Repeatable
元注解@Retention
应用到一个注解上用于说明该注解的生命周期,其取值如下:
1.RetentionPolicy.SOURCE注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视
2.RetentionPolicy.CLASS注解只保留到编译进行的时候,它并不会被加载到JVM中,默认方式
3.RetentionPolicy.RUNTIME注解可以保留到程序运行的时候,它会被加载到JVM中,所以在程序运行时可以获取到它们
//@Retention(RetentionPolicy.SOURCE) // 表示下面的注解在源代码中有效
//@Retention(RetentionPolicy.CLASS) // 表示下面的注解在字节码文件中有效,默认方式
@Retention(RetentionPolicy.RUNTIME) // 表示下面的注解在运行时有效
// 若一个注解中没有任何成员,则这样的注解叫做标记注解/标识注解
public @interface MyAnnotation {
//public String value(); //声明一个String类型的成员本类,名字为value 类型有要求
public String value() default "123"; //声明一个String类型的成员本类,名字为value
public String value2();
}
元注解@Documented
使用Javadoc工具可以从程序源代码中抽取类、方法、成员等注释形成一个和源代码配套的API帮助文档,而该工具抽取时默认不包括注解内容
@Documented用于指定被该注解将被javadoc工具提取成文档
定义为@Documented的注解必须设置Retention值为RUNTIME
//@Retention(RetentionPolicy.SOURCE) // 表示下面的注解在源代码中有效
//@Retention(RetentionPolicy.CLASS) // 表示下面的注解在字节码文件中有效,默认方式
@Retention(RetentionPolicy.RUNTIME) // 表示下面的注解在运行时有效
@Documented // 表示下面的注解信息可以被javadoc工具提取到API文档中,很少使用
// 若一个注解中没有任何成员,则这样的注解叫做标记注解/标识注解
public @interface MyAnnotation {
//public String value(); //声明一个String类型的成员本类,名字为value 类型有要求
public String value() default "123"; //声明一个String类型的成员本类,名字为value
public String value2();
}
// 表示将标签MyAnnotation贴在Person类的代码中,使用注解时采用 成员参数名 = 成员参数值, ...
//@MyAnnotation(value = "hello",value2 = "hello world")
@MyAnnotation(value2 = "world")
public class Person {
/**
* name 是用于描述姓名的成员变量
*/
private String name;
/**
* age是用于描述年龄的成员变量
*/
private int age;
/**
* 编程实现无参构造方法
*/
public Person() {
}
/**
* 编程实现有参构造方法
* @param name
* @param age
*/
public Person(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 自定义成员方法实现特征的获取和修改
* @return
*/
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;
}
}
元注解@Target
@Target用于指定被修饰的注解能用于哪些元素的修饰,取值如下:
ElementType.ANNOTATION_TYPE | 可以给一个注解进行注解 |
ElementType.CONSTRUCTOR | 可以给构造方法进行注解 |
ElementType.FIELD | 可以给属性进行注解 |
ElementType.LOCAL_VARIABLE | 可以给局部变量进行注解 |
ElementType.METHOD | 可以给方法进行注解 |
ElementType.PACKAGE | 可以给一个包进行注解 |
ElementType.PARAMETER | 可以给方法内的参数进行注解 |
ElementType.TYPE | 可以给类型进行注解,如:类、接口、枚举 |
//@Retention(RetentionPolicy.SOURCE) // 表示下面的注解在源代码中有效
//@Retention(RetentionPolicy.CLASS) // 表示下面的注解在字节码文件中有效,默认方式
@Retention(RetentionPolicy.RUNTIME) // 表示下面的注解在运行时有效
@Documented // 表示下面的注解信息可以被javadoc工具提取到API文档中,很少使用
// 表示下面的注解可以用于类型、构造方法、成员变量、成员方法的修饰
@Target({/*ElementType.TYPE,*/ElementType.CONSTRUCTOR,ElementType.FIELD,ElementType.METHOD,ElementType.PARAMETER})
// 若一个注解中没有任何成员,则这样的注解叫做标记注解/标识注解
public @interface MyAnnotation {
//public String value(); //声明一个String类型的成员本类,名字为value 类型有要求
public String value() default "123"; //声明一个String类型的成员本类,名字为value
public String value2();
}
// 表示将标签MyAnnotation贴在Person类的代码中,使用注解时采用 成员参数名 = 成员参数值, ...
//@MyAnnotation(value = "hello",value2 = "hello world")
//@MyAnnotation(value2 = "world")
public class Person {
/**
* name 是用于描述姓名的成员变量
*/
@MyAnnotation(value2 = "1")
private String name;
/**
* age是用于描述年龄的成员变量
*/
private int age;
/**
* 编程实现无参构造方法
*/
@MyAnnotation(value2 = "2")
public Person() {
}
/**
* 编程实现有参构造方法
* @param name
* @param age
*/
public Person(@MyAnnotation(value2 = "4") String name, int age) {
this.name = name;
this.age = age;
}
/**
* 自定义成员方法实现特征的获取和修改
* @return
*/
@MyAnnotation(value2 = "3")
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;
}
}
元注解@Inherited
@Inherited并不是说注解本身可以继承,而是说如果一个超类被该注解标记过的注解进行注解时,如果子类没有被任何注解应用时,则子类就继承超类的注解
//@Retention(RetentionPolicy.SOURCE) // 表示下面的注解在源代码中有效
//@Retention(RetentionPolicy.CLASS) // 表示下面的注解在字节码文件中有效,默认方式
@Retention(RetentionPolicy.RUNTIME) // 表示下面的注解在运行时有效
@Documented // 表示下面的注解信息可以被javadoc工具提取到API文档中,很少使用
// 表示下面的注解可以用于类型、构造方法、成员变量、成员方法的修饰
@Target({ElementType.TYPE,ElementType.CONSTRUCTOR,ElementType.FIELD,ElementType.METHOD,ElementType.PARAMETER})
@Inherited // 表示下面的注解所修饰的类中的注解使用可以被子类继承
// 若一个注解中没有任何成员,则这样的注解叫做标记注解/标识注解
public @interface MyAnnotation {
//public String value(); //声明一个String类型的成员本类,名字为value 类型有要求
public String value() default "123"; //声明一个String类型的成员本类,名字为value
public String value2();
}
// 表示将标签MyAnnotation贴在Person类的代码中,使用注解时采用 成员参数名 = 成员参数值, ...
//@MyAnnotation(value = "hello",value2 = "hello world")
@MyAnnotation(value2 = "world")
public class Person {
/**
* name 是用于描述姓名的成员变量
*/
@MyAnnotation(value2 = "1")
private String name;
/**
* age是用于描述年龄的成员变量
*/
private int age;
/**
* 编程实现无参构造方法
*/
@MyAnnotation(value2 = "2")
public Person() {
}
/**
* 编程实现有参构造方法
* @param name
* @param age
*/
public Person(@MyAnnotation(value2 = "4") String name, int age) {
this.name = name;
this.age = age;
}
/**
* 自定义成员方法实现特征的获取和修改
* @return
*/
@MyAnnotation(value2 = "3")
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;
}
}
// 也就是可以继承Person类的注解
public class Student extends Person{
}
元注解@Repeatable
@Repeatable表示自然可重复的含义,从Java8开始增加的新特性
从Java8开始对元注解@Target的参数类型ElementType枚举值增加了两个:
1.ElementType.TYPE_PARAMETER表示该注解能写在类型变量的声明语句中,如:泛型
2.ElementType.TYPE_USE表示该注解能写在使用类型的任何语句中
/**
* @auther weiwei
* @date 2021/4/3 16:13
* @description 自定义注解用于描述人物的角色
*/
@Repeatable(value = ManTypes.class)
@Target(ElementType.TYPE_USE)
public @interface ManType {
String value() default "";
}
/**
* @auther weiwei
* @date 2021/4/3 16:16
* @description 自定义注解描述多种角色
*/
@Target(ElementType.TYPE_USE)
public @interface ManTypes {
ManType[] value();
}
@ManType(value = "职工")
@ManType(value = "超人")
//@ManTypes({@ManType(value = "职工"),@ManType(value = "超人")}) // 在Java8以前处理多个注解的方式
public class Man {
public static void main(String[] args) {
int ia = 97;
char c1 = (@ManType char) ia;
}
}
常见的预制注解
预制注解——Java语言自身提供的注解
@author | 标明开发该类模块的作者,多个作者之间使用,分割 |
@version | 标明该类模块的版本 |
@see | 参考转向,也就是相关主题 |
@since | 从哪个版本开始增加的 |
@param | 对方法中某参数的说明,如果没有参数就不能写 |
@return | 对方法返回值的说明,如果方法的返回值是void就不能写 |
@exception | 对方法可能抛出的异常进行说明 |
@Override | 限定重写父类方法,该注解只能用于方法 |
@Deprecated | 用于表示所修饰的元素(类,方法等)已过时 |
@SuppressWarnings | 抑制编译器警告 |
public class Man {
@Deprecated // 表示该方法已经过时,不建议使用
public void show(){
System.out.println("这个方法马上过时了!");
}
}
public class ManTest {
public static void main(String[] args) {
Man man = new Man();
man.show();
}
}