面向对象进阶
1.static
表示静态,可以修饰成员方法、成员变量
静态变量
static String teacherName;
调用方式:
- 类名调用(推荐)
- 对象名调用
静态变量随着类的加载而加载,优先于对象出现的,不属于对象,属于类
静态方法
多用在测试类和工具类中
Javabean类中很少会用
调用方式:
- 类名调用(推荐)
- 对象名调用
类的种类(目前):
- Javabean类:用来描述一类事物的类,需要:私有化成员变量,书写空参构造方法,书写带全部参数的构造方法,针对每一个私有化成员变量写对应的get、set方法
- 测试类:用来检查其他类是否书写正确,带有main方法的类,是程序的入口
- 工具类:不是用来描述一类事物的,而是帮我们做一些事情的类,应该注意:
①类名要见名知意
②私有化构造方法(不让外界创建它的对象)
③方法都定义为静态,方便调用
工具类举例:
public class ArrayUtil(){
private ArrayUtil(){
}
public static String printArr(int[] arr){
StringBuilder sb = new StringBuilder();
sb.append("[");
for (int i = 0; i < arr.length; i++){
if(i = arr.length - 1){
sb.append(arr[i]);
}else{
sb.append(arr[i]).append(",");
}
}
sb.append("]");
return sb.toString();
}
}
static注意事项
- 静态方法中,只能访问静态
- 非静态方法可以访问所有
- 静态方法中没有this关键字
2.继承
基本语法:
public class Student extends Person{}
继承特点
- 只支持单继承
- 不支持多继承
- 支持多层继承
每一个类都直接或者间接的继承于object
子类只能访问父类中非私有的成员
子类能继承父类的内容
- 构造方法:
不能被继承 - 成员变量:
可以被继承 私有成员变量能被继承但是不能直接使用 - 成员方法:
非私有:能 私有:不能
虚方法表:非private、非static、非final
父类会把虚方法给子类
继承中:成员变量的访问特点
就近原则
public class Fu {
String name = "Fu"
}
public class Zi extends Fu{
String name = "zi";
public void ziShow(){
String name = "ziShow";
System.out.println(name); //"ziShow"
System.out.println(this.name); //"zi",先去本类成员位置找,再去父类成员位置找
System.out.println(super.name); //"Fu"
}
}
方法的重写:
- 当父类的方法不能满足子类现在的需求时,需要进行方法重写
@Override重写注解:
- @Override是放在重写后的方法上,校验子类重写时语法是否正确
- 加上注解后如果有红色波浪线,表示语法错误
@Override
public void eat(){
}
本质:如果发生了重写,则会覆盖虚方法表中的方法
注意事项:
- 重写方法的名称,形参列表必须和父类中的一致
- 子类重写父类方法时,访问权限子类必须大于等于父类
- 子类重写父类方法时,返回值类型子类必须小于等于父类
- 建议:重写的方法尽量和父类保持一致
- 只有被添加到虚方法表中的方法才能被重写
继承中:构造方法的访问特点
特点:
- 父类中的构造方法不会被子类继承
- 子类中所有的构造方法默认先访问父类中的无参构造,再执行自己
为什么:
- 子类在初始化的时候,有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据
- 子类初始化之前,一定要调用父类构造方法先完成父类数据空间的初始化
调用父类方法方式:
- 子类构造方法的第一行语句默认都是: super(),不写也存在,且必须在第一行
- 如果想调用父类有参构造,必须手动写super进行调用,如下图:
this、super使用总结
this:理解为一个变量,表示当前方法调用者的地址值
this(null, o, "传智大学");//调用本类其他构造方法,给默认值时使用
1.经理
成员变量:工号,姓名,工资,管理奖金成员方法:工作(管理其他人),吃饭(吃米饭)
2.厨师
成员变量:工号,姓名,工资成员方法:工作(炒菜),吃饭(吃米饭)
根据上述信息写一个标准的javabean类;
父类:
public class Employee{
private String id;
private String name;
private double salary;
public Employee(){
}
public Employee(String id, String name, double salary){
this.id = id;
this.name = name;
this.salary = salary;
}
public String getId(){
return id;
}
public void setId(String name){
this.name = name;
}
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public Double getSalary(){
return salary;
}
public void setSalary(Double salary){
this.salary = salary;
}
public void work(){
System.out.println("工作");
}
public void eat(){
System.out.println("吃饭");
}
}
管理者:
public class Manager extends Employee{
private double bouns;
public Manager(){
}
public Manager(String id, String name, double salary, double bouns) {
//带全部参数的构造(父类 + 子类)
super(id,name,salary);
this.bouns = bouns;
}
public double getBouns() {
return bouns;
}
public void setBouns(double bouns) {
this.bouns = bouns;
}
@Override
public void work(){
System.out,println("管理其他人");
}
}
厨师:
public class Cook extends Employee{
public Cook(){
}
public Cook(String id, String name, double salary, double bouns) {
super(id,name,salary);
}
@Override
public void work(){
System.out,println("炒菜");
}
}
测试类:
public class Test {
public static void main(String[] args){
Manager m = new Manager("heima001", "水", 15, 8000);
System.out.println(m.getId() + "," + m.getName() + "," +
m.getSalary() + "," + m.getBouns());
m.work();
m.eat();
Cook c = new Cook();
c.setId("heima002");
c.setName("雅洁");
c.setSalary(8eee);
System.out.println(c.getId() + "," + c.getName() + "," + c.getSalary());
c.work();
c.eat();
}
}
3.多态
同类型的对象,表现出的不同形态
多态的表现形式:
- 父类类型 对象名称 = 子类对象
多态的前提:
- 有继承/实现关系
- 有父类引用指向子类对象
- 有方法重写
好处:
使用父类型作为参数,可以接收所有子类对象体现多态的扩展性与便利
多态中调用成员的特点
public class Test {
public static void main(String[] args) {
//创建对象(多态方式)
//Fu f = new Zi();
Animal a = new Dog();
//调用成员变量:编译看左边,运行也看左边
//编译看左边:javac编译代码的时候,会看左边的父类中有没有这个变量,如果有,编译成功,如果没有编译失败
//运行也看左边: java运行代码的时候,实际获取的就是左边父类中成员变量的值
System.out.println(a.name);//动物
//调用成员方法:编译看左边,运行看右边
//编译看左边:javac编译代码的时候,会看左边的父类中有没有这个方法,如果有,编译成功,如果没有编译失败。
//运行看右边:java运行代码的时候,实际上运行的是子类中的方法
a.show();//Dog --- show方法
}
}
class Animal{
string name =“动物";
public void show(){
System.out.println("Animal --- show方法");
}
}
class Dog extends Animal{
String name ="狗";
@Override
public void show(){
System.out.println("Dog --- show方法");
}
}
class Cat extends Animal{
String name ="猫";
@Override
public void show(){
System.out.println("Cat --- show方法");
}
}
理解:
Animal a = new Dog();
现在用a去调用变量和方法,而a是Anima1类型的,所以默认都会从Animal这个类中去找
- 成员变量:在子类的对象中,会把父类的成员变量也继承下的,父: name 子: name
- 成员方法:如果子类对方法进行了重写,那么在虚方法表中是会把父类的方法进行覆盖的。
多态优势和弊端
优势:
- 在多态形式下,右边对象可以实现解耦合,便于扩展和维护
- 方法中,使用父类型作为参数,可以接收所有子类对象(比较关键)
弊端:
- 不能调用子类的特有功能
报错的原因:
当调用成员方法的时候,编详看左边,运行看右边那么在编译的时候会先检查左边的父类中有没有这个方法,如果没有直接报错
解决方案:
把调用者变回子类类型。 (自动类型转换、强制类型转换)
Animal a = new Dog();
a.eat();
Dog d = (Dog) a;
d.lookHome();
注意:不要转换成其他类的类型
if(a instanceof Dog d){
d.lookHome();
}else if(a instanceof Cat c){
c.catchMouse();
}else{
System.out.println("没有这个类型,无法转换");
}
//先判断a是否为Dog类型,如果是,则强转成Dog类型,转换之后变量名为d,如果不是,则不强转,结果直接是false
把此处练习写一下
4.包
包就是文件夹。用来管理各种不同功能的lava类,方便后期代码维护
包名的规则:公司域名反写+包的作用,需要全部英文小写,见名知意。 com.itheima.domain
全类名(全限定名): com.itheima.domain.Student
使用其他类时,需要使用全类名
import 关键字
使用其他类的规则
- 使用同一个包中的类时,不需要导包
- 使用java.lang包中的类时,不需要导包。
- 其他情况都需要导包
- 如果同时使用两个包中的同名类,需要用全类名
5.final
最终的,不能被改变的
修饰:方法、类、变量
- 表明该方法是最终方法,不能被重写
- 表明该类是最终类,不能被继承
- 叫做常量,只能被赋值一次
常量的命名规范: - 单个单词:全部大写
- 多个单词:全部大写,单词之间用下划线隔开
细节: - final修饰的变量是基本类型:那么变量存储的数据值不能发生改变
- final修饰的变量是引用类型: 那么变量存储的地址值不能发生改变,对象内部的可以改变
6.权限修饰符
权限修饰符的使用规则:
实际开发中,一般只用private和public
- 成员变量私有
- 方法公开
特例:如果方法中的代码是抽取其他方法中共性代码,这个方法一般也私有
7.代码块
- 局部代码块
- 构造代码块
- 写在成员位置的代码块
- 作用: 可以把多个构造方法中重复的代码抽取出来
- 执行时机:我们在创建本类对象的时候会先执行构造代码块再执行构造方法
{ System.out.println("构造代码块") } { public Student(){ } public Student(String name, int age){ this.name = name; this.age = age; } }
一般使用下面两种方法解决构造方法中重复代码问题
- 静态代码块行※
格式:static{}
执行时机:随着类的加载而加载的,并且只执行一次。
应用场景:做一些数据初始化且只执行一次。
8.抽象类
抽象方法的子类必须重写,不然直接报错,抽象方法所在的类,就叫抽象类
- 抽象方法:将共性的行为 (方法) 抽取到父类之后。由于每一个子类执行的内容是不一样所以,在父类中不能确定具体的方法体该方法就可以定义为抽象方法。
- 抽象类:如果一个类中存在抽象方法,那么该类就必须声明为抽象类
public abstract class Person {
public abstract void work();
}
注意事项:
- 抽象类不能实例化
- 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
- 可以有构造方法、
- 作用:当创建子类对象的的,给属性进行赋值的。
- 抽象类的子类
- 要么重写抽象类中的所有抽象方法
- 要么是抽象类
9.接口
行为的抽象
定义和使用:
接口用关键字interface来定义
- public interface 接口名{}
接口不能实例化
接口和类之间是实现关系,通过implements关键字表示
- public class 类名 implements
接口名接口的子类(实现类)
- 要么重写接口中的所有抽象方法
- 要么是抽象类
注意1:接口和类的实现关系,可以单实现,也可以多实现。
public class 类名 implements 接口名1,接口名2
注意2:实现类还可以在继承一个类的同时实现多个接口
public class 类备 extends 父类implements 接口名1,接口名2
public interface Swim{
public abstract void Swim();
}
public class Dog extends Animal implements Swim{
}
接口中成员的特点
- 成员变量:
只能是常量
默认修饰符:public static final
构造方法:没有 - 成员方法:
只能是抽象方法
默认修饰符:public abstract
接口和类之间的关系
- 类和类的关系:
继承关系,只能单继承,不能多继承,但是可以多层继承 - 类和接口的关系:
实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口 - 接口和接口的关系:
继承关系,可以单继承,也可以多继承
细节: 如果实现类实现了最下面的子接口,那么就需要重写所有的抽象方法
JDK8以后接口中新增的方法
JDK8的新特性: 接口中可以定义有方法体的方法。 (默认、静态)
JDK9的新特性: 接口中可以定义私有方法
①:允许在接口中定义默认方法,需要使用关键字 default 修饰
- 作用: 解决接口升级的问题
接口中默认方法的定义格式:
- 格式: public default 返回值类型 方法名(参数列表){}
- 范例: public default void show(){}
注意事项:
- 默认方法不是抽象方法,所以不强制被重写。但是如果被重写,重写的时候去掉default关键字
- public可以省略,default不能省略
- 如果实现了多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法进行重写
②:允许在接口中定义定义静态方法,需要用static修饰
接口中静态方法的定义格式:
- 格式:public static 返回值类型 方法名(参数列表){}
- 范例: public static void show(){}
注意事项:
- 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
- public可以省略static不能省略
- 接口中的静态方法不能重写
③:允许在接口中定义私有方法,需要用private修饰
接口中私有方法的定义格式:
-
格式1:private 返回值类型 方法名(参数列表){}
范例1: private void show(){}
普通的私有方法,给默认方法服务的 -
格式2:private static 返回值类型 方法名(参数列表){}
范例2: private static void method(){} -
静态的私有方法,给静态方法服务的*
接口的应用
- 接口代表规则,是行为的抽象。想要让哪个类拥有一个行为,就让这个类实现对应的接口就可以了
- 当一个方法的参数是接口时,可以传递接口所有实现类的对象,这种方式称之为接口多态
接口类型 j = new 实现类对象 () ;
适配器设计模式
解决接口与接口实现类之间的矛盾问题
- 当一个接口中抽象方法过多,但是我只要使用其中一部分的时候,就可以适配器设计模式
- 书写步骤:
- 编写中间类XXXAdapter,实现对应的接口
- 对接口中的抽象方法进行空实现
- 让真正的实现类继承中间类,并重写需要用的方法
- 为了避免其他类创建适配器类的对象,中间的适配器类用abstract进行修饰
- 若是实现类有父类,可以让中间类去继承
10.内部类
类的五大成员之一(属性、方法、构造方法、代码块)
- 内部类表示的事物是外部类的一部分
- 内部类单独出现没有任何意义
内部类的访问特点:
- 内部类可以直接访问外部类的成员,包括私有
- 外部类要访问内部类的成员,必须创建对象
public void show(){
//是打印调用者车的名字: 宾利
System.out.printIn(this.carName);
Engine e = new Engine();
System.out .printIn(e.engineName);
}
class Engine{
String engineName;
int engineAge;
public void show(){
System.out.printIn(engineName);
System.out.printIn(carName);
}
}
内部类的分类
- 成员内部类
- 静态内部类
- 局部内部类
- 匿名内部类 -->掌握,其余了解
成员内部类
写在成员位置的,属于外部类的成员。
注意:
- 可以修饰成员变量的修饰符,都修饰成员内部类
- JDK16以后可以定义静态变量
获取成员内部类对象的两种方式:
- 外部类编写方法,对外提供内部类对象(private)
public class Outer{
String name;
public class Inner{
static in a = 10;
}
public Inner getInstance(){
return new Inner
}
}
Outer o = new Outer();
System.out.printIn(o.getInstance());
- 直接创建
Outer.Inner oi = new Outer().new Inner();
成员内部类获取外部类的成员变量
public class Outer {
private int a = 10;
class Inner {
private int a = 20;
public void show() {
int a = 30![img](/i/l/?n=23&i=blog/2709896/202307/2709896-20230731193832953-670122904.png);
System.out.println(a); //30
System.out.println(this.a); //20
System.out.println(Outer.this.a);//10
}
}
}
静态内部类
静态内部类只能访问外部类中的静态变量和静态方法,如果想要访问非静态的需要创建对象。
创建静态内部类对象的格式:外部类名.内部类名 对象名= new 外部类名.内部类名():
调用非静态方法的格式: 先创建对象,用对象调用
调用静态方法的格式:外部类名.内部类名.方法名()
局部类
- 将内部类定义在方法里面就叫做局部内部类,类似于方法里面的局部变量。
- 外界是无法直接使用局部内部类,需要在方法内部创建对象并使用。
- 该类可以直接访问外部类的成员,也可以访问方法内的局部变量。
匿名内部类
匿名内部类本质上就是隐藏了名字的内部类,可以写在成员位置,也可以写在局部位置
格式:
new 类名或接口名(){
重写方法;
};
- 继承/实现关系
- 方法的重写
- 创建对象
new Swim(){
@Override
public void swim() {
System.out.printIn("重写了游泳的方法");
}.swim();
//Swim是一个游泳的接口
//整体可以理解为Swim接口的实现类对象,可以调用swim方法
Swim s = new Swim(){
@Override
public void swim() {
System.out.printIn("重写了游泳的方法");
};
s.swim();
//也可以new一个对象。
method(
new Animal() {
@Override
public void eat() {
System.out.printIn("狗吃骨头");
}
}
)
public static void method(Animal a){
//Animal a = 子类对象 多态
a.eat();//编译看左边,运行看右边
}
不然需要创建一个子类继承Animal,然后创建子类对象,传递给method方法
标签:name,子类,day3,接口,父类,方法,public From: https://www.cnblogs.com/shuijibaobao/p/17592117.html