前言
类与接口
文章目录
一、面向过程与面向对象
1、引例
- 背景:做一个盒子;
- 面向过程:开始先不去想做成一个什么样的盒子,随机选取工具制作,最后做成什么样就是什么样;
- 面向对象:开始先预想好做成一个什么样的盒子,然后再去找对应的工具去做;
2、面向过程特征
- 以过程为中心;
- 将问题分解成详细的步骤,通过函数去实现每一个步骤,并依次调用;
3、面向对象特征
- 封装性:对外部不可见;
- 继承:扩展类的功能;
- 多态:方法的重载、对象的多态性;
二、类与对象
1、类
- 类的本质是一种数据类型,通过将
变量和函数打包在一起,组成一种新的数据类型; - 使用方式与基本数据类型相同;
- Java 中所有的程序都需要以类的方式来编写和执行(在 Java 语言的世界里,所有的 Java 程序都必须要以类的形式存在;);
2、类与对象的关系
-
类是对某一类事物的描述,是抽象的概念上的意义,无法具体化,没有可操作性,定义好类后并不能立即使用;
-
对象是实际存在的该类事物的每一个个体,也被称为实例;
-
类是一种数据类型;对象为类中的每一个变量;
class Person { 类的命名:类的首字母大写; String name; int age; public void tell() { System.out.println("姓名:" + name + " " + "性别:" + age); } } public class Demo { public static void main(String[] args) { // 创建对象并为对象开辟内存空间; // ①创建对象,此时P为Person的一个对象,此时并不能使用,需实例化 Person per = null; per = new Person(); // 实例化,通过new开辟内存空间 // 上面的两个操作可以合并,声明并实例化 Person per = new Person(); } } -
内存划分:
Person p: 开辟栈内存空间,此时不能使用,需要通过 new 开辟堆内存空间后才可使用;
三、文件声明
- 一个源文件中只能有一个
public类,可以有多个非public类; - 源文件名应与
public类名完全一致; - 包(
package):即文件夹,后面跟着当前文件所在的文件夹路径; - 每个源文件中内容顺序:
package xxx --> import xxx --> class; - 在各个文件中写各种类,通过类之间的相互调用,共同实现复杂的项目逻辑;
四、修饰符
1、作用
- 修饰符可以修饰类,也可修饰类中的元素;
- 类中元素包括变量和函数
- 变量称为成员变量,函数称为成员方法;
2、分类
- 1)修饰成员变量/成员方法
-
public- 所有对象都可以访问,表示全局的,具有公共访问权限;
- 使用
public修饰变量/方法,则可以在当前项目中任何地方都可以访问到;
-
private- 表示私有的,只有本类内部可以访问,离开本类之后,类外不能对其进行访问;
- 将变量用 private 来修饰,表示该变量只能在所在的这个类中来使用,防止类外对其修改;
class Student { public int x; private int y; } public class Main { public static void main(String[] args) { Student student = new Student(); student.x = 3; // 不报错 student.y = 4; // 报错 } }
-
protected:同一包或子类可以访问;对于类的外部是不可见的,只能在类的内部或继承的子类中访问; -
不加修饰符:在同一个包内(同一文件夹下)都可以访问; -
static-
static 修饰变量- 静态变量(公有)
- 可以通过实例对象调用,最好通过 类名. 的方式来调用;
- 静态变量与普通成员变量的区别
- 静态成员变量为所有实例所共有的;
- 普通成员变量为每个实例所独有的;
class Student { public static int x = -1; public int y = -1; } public class Main { Student student1 = new Student(), student2 = new Student(), student3 = new Student(); student1.x = 2; System.out.printf("%d %d %d", student1.x, student2.x, student3.x); // 均为2 Student.x; // 通过类名来访问 }
- 静态变量(公有)
-
static 修饰函数-
静态方法(公有);可以通过实例对象调用,最好通过 类名. 的方式来调用;
-
静态函数与普通成员函数的区别
- 静态成员函数是所有对象所共有的;
- 普通成员函数为每个对象所独有的;
-
使用
static修饰方法时,只能访问其它static声明的属性和方法,而非static声明的属性和方法是不能访问的(因为静态方法通过类名来访问,如果访问普通变量/方法无法得知访问的是哪个对象的变量和函数,不具有清晰的确定性));- 静态方法只能访问静态方法/静态变量;
- 普通方法既可以访问静态方法/静态变量,也可以访问普通方法/普通变量;
-
静态方法被 static 修饰,它们属于类而不是实例,因此在静态方法中只能访问类级别的静态成员,而不能直接访问实例级别的成员。这是因为静态方法在运行时并不知道具体的实例,因此无法访问实例级别的成员
public class MyClass { static int staticVar = 10; int instanceVar = 20; static void staticMethod() { System.out.println(staticVar); // 可以访问静态变量 // System.out.println(instanceVar); // 错误!不能访问实例变量 } } -
普通方法既可以访问静态方法/静态变量,也可以访问普通方法/普通变量:普通方法可以访问类级别的静态成员和实例级别的成员,因为普通方法在调用时会通过实例来确定具体的成员。不被 static 修饰的方法是属于对象的。
public class MyClass { static int staticVar = 10; int instanceVar = 20; static void staticMethod() { System.out.println(staticVar); // 可以访问静态变量 // System.out.println(instanceVar); // 错误!不能访问实例变量 } void instanceMethod() { System.out.println(staticVar); // 可以访问静态变量 System.out.println(instanceVar); // 可以访问实例变量 } }
-
-
static 修饰类(静态内部类)
静态内部类的实例:组织性和可读性: 将相关的类组织在一起,使得代码更加清晰和易于理解。静态内部类通常与外部类密切相关,因此将其定义为静态的可以减少类之间的耦合,提高代码的可读性和维护性。
封装性: 静态内部类可以访问外部类的静态成员,但不能直接访问外部类的非静态成员。这种封装性可以帮助保持外部类的私有性,并防止不必要的访问。
代码整洁性: 将相关的类放在一起,可以减少包的混乱性。静态内部类可以作为外部类的一部分,但不会增加包的大小或者混淆命名空间public class OuterClass { private static int outerStaticVar = 10; private int outerInstanceVar = 20; // 静态内部类 public static class StaticInnerClass { void innerMethod() { System.out.println("Accessing outer static variable: " + outerStaticVar); // System.out.println("Accessing outer instance variable: " + outerInstanceVar); // 错误!无法访问非静态成员 } } }
-
-
五、封装
1、目的
- 使当前类的某些属性和方法在类外不可见;
2、实现
-
将成员变量(属性)修饰为
private; -
实现该属性的
get和set方法; -
类外访问被
private修饰的变量时,通过set函数赋值,通过get函数取值;public class Point { private int x; private int y; // 构造方法 public Point(int x, int y) { this.x = x; // this. :访问当前的成员变量,成员变量x this.y = y; // this.:访问当前成员变量,成员变量y } public void setX(int x) { this.x = x; } public void setY(int y) { this.y = y; } public int getX() { return x; } public int getY() { return y; } public String toString() { return String.format("(%d, %d)", x, y); // 将当前点转换成字符串 } }
六、构造方法
-
作用:用于创建对象时初始化属性;
-
构造方法无需调用,实例化过程中
new时会自动调用构造方法; -
如果类中没有明显的构造方法,程序在编译时会自动创建一个无参且什么都不做的构造方法;
-
构造方法也可以重载;
public class Point { private int x; private int y; // 构造函数 public Point(int x, int y) { // 局部变量 x,y,参数x,y this.x = x; // 将局部变量x赋值给成员变量x this.y = y; // 将局部变量y赋值给成员变量y } }
七、继承
1、作用
- 继承的逻辑常用,主要目的是为了扩展父类的功能,实现代码复用,减少代码量;
- 关键字为
extends(扩展)
2、引例
-
背景 1:一个游戏;- 所有英雄有共同的特点(血条、魔法值、技能),每个英雄又有各自不同的特点();
- 为了减少代码量,将公共的特点变成一个基类,每个英雄角色将公共特点从基类中继承,然后在此基础上增加自己的特点;
-
背景 2:点的基础上添加一个属性- 有一个
Point(点)类,想实现一个新的点类,含有color这个属性; - 新的点也属于点,新类可以将基类所有的内容继承过来,在基础上加一个新的参数;
- 有一个
3、注意点
1)每个类只能继承自一个类
- 继承过程中,子类会继承基类中所有的成员变量和成员函数;
2)super 关键字
- 作用:
- 强行调用父类的方法
- 使用
-
在当前类调用基类(父类)时, 通过
super关键字,super代表父类; -
调用父类构造函数通过
super(); -
new 子类对象时,子类和父类的构造方法都会执行,编译器会自动加上
super(),自动调用父类的构造方法;class Father { public Father() { System.out.println("父类的构造方法"); } } class Son extends Father { public Son() { // 编译器会自动加上 super() System.out.println("子类的构造方法"); } } public class Main { public static void main(String[] args) { Son s = new Son(); // 输出:"父类的构造方法“ 与 ”子类的构造方法“,会先执行父类的构造方法 } }// 当子类和父类中有同名函数时,会优先使用子类的; // 父类 class Point { private int x; private int y; // 构造函数 public Point(int x, int y) { // 局部变量 x,y,参数x,y this.x = x; // this. :访问当前的成员变量,成员变量x this.y = y; // this.:访问当前成员变量,成员变量y } public void setX(int x) { this.x = x; } public void setY(int y) { this.y = y; } public int getX() { return x; } public int getY() { return y; } public String toString() { return String.format("(%d, %d)", x, y); // 将当前点转换成字符串 } }// 子类 class ColorPoint extends Point { private String color; // 当前类自己的属性 // 当前类的构造函数 public ColorPoint(int x, int y, String color) { // 调用父类的构造函数 super() super(x, y); // 将父类中的x和y赋成当前的局部变量亮x和y this.color = color; // 赋值当前成员变量 } // 实现当前类独有的函数 public void setColor(String color) { this.color = color; } // 性质:当子类和父类中有同名函数时,会优先使用子类的 public String toString() { // 子类和父类中有同名的函数时会优先使用子类的 return String.format("(%d, %d, %s)", super.getX(), super.getY(), this.color); // 调用父类中的x和y } }
-
3)重写与重载
- 重写
- 继承中有重写的概念,即子类定义了和父类同名的方法;
- 定义:方法名称相同,返回值类型相同,参数相同;
- 子类重写了父类的方法,如果不强行去调用父类方法是不会执行;
class A { public void tell() { System.out.println("我是tell方法"); } } class B extends A { // 重写 public void tell() { System.out.println("我重写了tell方法"); } } public class Main { public static void main(String[] args) { B b = new B(); b.tell(); // 只输出:我重写了tell方法,不会执行父类的tell方法(因为当子类和父类中有同名函数时,会优先使用子类的) } }- 需求:必须执行父类中的 tell 方法,可以通过
super关键字来调用;
class A { public void tell() { System.out.println("我是tell方法"); } } class B extends A { // 重写 public void tell() { super.tell(); // 调用父类tell方法 System.out.println("我重写了tell方法"); } } public class Main { public static void main(String[] args) { B b = new B(); b.tell(); // 输出:我是tell方法 与 我重写了tell方法,不会执行父类的tell方法 } }
4)子类对象实例化过程
- 会自动先调用的父类构造方法,之后才调用子类的构造方法;
八、多态
1、引入
-
因为子类继承了父亲的属性和方法,并且可以被视为父亲的一种特殊形式;
-
Java 中子类对象可以赋值给父类类型的变量,允许使用父类类型的变量来引用子类类型的对象;具体为,在 Java 中,如果一个类(子类)继承自另一个了类(父类),那么子类的对象可以被赋值给父亲类型的变量;
// Dog 类继承自 Animal 类,并且重写了 sound() 方法。 //在 Main 类的 main() 方法中,通过将 Dog 类的对象赋值给 Animal 类型的变量 animal,实现了多态性 //尽管变量的类型是父亲类型,但是实际调用的是子类的方法,输出结果为 Dog barks class Animal { public void sound() { System.out.println("Animal makes a sound"); } } class Dog extends Animal { @Override public void sound() { System.out.println("Dog barks"); } } public class Main { public static void main(String[] args) { Animal animal = new Dog(); // 子类对象赋值给父亲类型的变量 animal.sound(); // 调用的是子类的方法 } }
2、作用
-
同一种类型的变量,调用同一个函数会有不同的行为;
-
知识点:父类引用可以指向子类对象




-
Point2虽然是父类类型,但是对象是子类对象; -
Point1和Point2同为父类类型,调用toString()时结果不一样,Point1调用父类的toString(),Point2调用子类的toString(); -
一个游戏中几百个英雄,初始时每个英雄都会说一句话,如果不用多态的话,需要调用每一个英雄的
greet方法,此时需要判断具体的哪个英雄,然后再执行相应的操作; 利用多态,将每一个英雄(对象)用同一个基类类型来定义即可,省去判断,每次调用greet函数时会根据具体对应的的对象来执行相应的 greet 函数。
-



九、接口
- 接口和类的概念类似,但其主要是作为一个工具用来定义一个类中必须要包含哪些函数;
- 定义:一个类实现一个接口,必须要实现这个接口中的所有函数;
- 接口的主要作用作为一种规范程序员的工具,按照既定的要求去实现,方便协作;
1、接口的定义
- 知识点:接口中的函数不添加修饰符,默认为
Public// 接口的定义 public interface Role { // 定义每个角色都需要包含的操作 void greet(); void move(); int getSpeed(); }
2、接口的继承
- 知识点:一个接口可以继承自多个接口
public interface Hero extends Role {
// 还包括Role接口中定义的函数
void attack();
}
3、接口的实现
-
接口的实现就是实现接口中定义的函数;
-
实现的方式通过类来实现;
-
角色定义成类,实现的功能定义成接口;
// 定义一个宙斯类实现Hero接口 class Zeus implements Hero { // 角色1:宙斯 private final String name = "Zeus"; // 当前英雄的名字 public void attack() { System.out.println(name + ": Attack!"); } // 实现Role中的函数:因为Hero继承自Role public void greet() { System.out.println(name + ": Hi!"); } public void move() { System.out.println(name + ": Move!"); } public int getSpeed() { return 10; } } -
一个类可以实现多个接口,每个接口用 , 隔开;定义两个接口,用一个类去实现;需要实现这两个类中所有的函数;
// 此处的Role接口不继承自Hero接口,是两个独立的接口 public class Zeus implements Hero, Role { ... }
4、接口的多态
-
同一类型的变量执行同一操作,实现不同的功能;
-
一个对象可以放到本类类型的变量上,也可以放到父类类型的变量上,也可以放到接口类型的变量上;
-
背景:多个英雄(类)去实现同一个接口;英雄:宙斯、雅典娜;通过将对象全部定义成接口类型;访问接口类型变量的某一操作时,会根据不同的对象执行不同的操作;
class Athena implements Hero { private final String name = "Athena"; public void attack() { System.out.println(name + ": Attack!!!"); } public void greet() { System.out.println(name + ": Hi!!!"); } public void move() { System.out.println(name + ": Move!!!"); } public int getSpeed() { return 10; } } public class Main { public static void main(String[] args) { Hero[] heros = {new Zeus(), new Athena()}; // 将对象定义成接口类型 for (Hero hero: heros) { // 每一个对象都是同一种类型 // 同一种类型,执行同一种操作,实现不同的逻辑,第一个hero调用宙斯的greet(),第二个hero调用雅典娜的greet() hero.greet(); } } }
-
接口可以用来实现回调机制,将方法作为参数传递给其他方法使用
// 我们定义一个接口,这个接口包含了一个方法,作为回调函数
public interface Callback {
void onTaskCompleted(String result);
}
// 然后,我们定义一个任务执行器,它接受一个Callback实例作为参数
public class TaskExecutor {
// 执行任务,并在完成后调用回调函数
public void executeTask(Callback callback) {
// 模拟任务执行...
String result = "任务完成";
// 假设任务执行完成,调用回调
callback.onTaskCompleted(result);
}
}
// 最后,我们实现Callback接口,并在其中定义任务完成后的操作
public class TaskCompletionHandler implements Callback {
@Override
public void onTaskCompleted(String result) {
System.out.println("任务结果:" + result);
}
public static void main(String[] args) {
TaskExecutor executor = new TaskExecutor();
Callback callback = new TaskCompletionHandler();
executor.executeTask(callback);
}
}
十、抽象类
- 包含一个抽象方法的类就是抽象类;
- 抽象方法
- 声明未被实现的方法,必须使用
abstract关键字声明;
- 声明未被实现的方法,必须使用
- 使用抽象类:抽象类不能直接进行实例化,必须通过其子类来实例化;通过子类(不是抽象类)实例化后,子类必须实现抽象类中的所有抽象方法;
// 定义 abstract class className { // 下面的不是必须的,均为可选项 属性 方法 抽象方法 }// 抽象类 abstract class Abs { private int age; public void tell() { } // 抽象方法 public abstract void say(); public abstract void print(); } // 子类继承抽象类 class AbsDemo extends Abs { public void say() { } public void print() { } } public class AbsDemo01 { public static void main(String[] args) { // 通过子类来使用抽象类 AbsDemo a = new AbsDemo(); a.say(); a.print(); } }
十一、补充
1、引用传递





2、this 关键字
-
访问类中的属性
- 用于和函数中同名的形参变量作区分;

-
调用当前类中的方法

-
通过
this调用类中的构造方法,注意必须放在第一行
-
表示当前对象;
p和this指同一个对象
3、匿名类
-
作用
- new 接口类型:是在创建对象时使用匿名类来实现某个接口
- 利用此种方式,可以直接在对象创建的地方定义接口的实现逻辑,而不需要显示地定义一个具体的类名来实现接口;此种方式适用于临时需求或只需要使用一次的情况,可以简化代码;


-
使用 { } 创建匿名内部类的好处是,我们可以在创建对象的同时对其进行定制化的扩展。

首先,创建了一个 Handler 对象,并使用 new Handler(Looper.myLooper()) 进行初始化。这里的 Looper.myLooper() 返回当前线程的 Looper 对象,用于处理消息队列。
接着,使用 {} 创建了一个匿名内部类,并将其作为 Handler 对象的扩展。在这个匿名内部类中,重写了 handleMessage(Message msg) 方法,用于处理消息。在 handleMessage(Message msg) 方法中,我们首先通过 super.handleMessage(msg) 调用了父类 Handler 的 handleMessage() 方法,以确保父类的默认处理逻辑得到执行。然后,可以在该方法中添加自定义的消息处理逻辑。
- 匿名对象
- 指没有被命名或没有被赋予变量引用的对象,是一种临时创建和使用对象的方式,通常用于简化代码或在特定情况下临时执行某些操作。
- 匿名对象的特点是在创建时不使用变量进行引用,而是直接在需要的地方创建对象,并在同一行代码中使用它。由于没有变量引用,匿名对象通常只能在创建的地方使用,无法在其他位置再次引用或重复使用。需要注意的是,由于匿名对象没有变量引用,一旦离开了创建的作用域或使用的代码块,该对象就会被垃圾回收机制回收。因此,在使用匿名对象时要注意对象的生命周期和作用域。

916

被折叠的 条评论
为什么被折叠?



