12、面向对象(高级)
2021/12/17 6:21:58
本文主要是介绍12、面向对象(高级),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
目录- 一、类变量和类方法
- 1、类变量(静态变量)
- 2、类方法(静态方法)
- 二、main方法语法解析
- 三、代码块{}
- 1、什么是代码块?
- 2、使用细节
- 3、代码块的好处
- 四、单例设计模式(静态方法和属性的经典使用)
- 五、final关键字
- 1、使用场景
- 2、使用细节
- 六、抽象类(abstract)
- 1、为什么需要抽象类?
- 2、抽象类的介绍
- 3、使用细节
- 七、模板设计模式——抽象类的最佳实践
- 1、基本介绍
- 2、作用
- 八、接口(interface)
- 1、为什么需要接口?
- 2、什么是接口?
- 3、应用场景
- 4、注意事项
- 5、接口与继承
- 6、接口的多态性
- 九、内部类
- 1、为什么需要内部类?
- 2、什么是内部类?
- 3、语法
- 4、内部类的分类
- 5、成员内部类
- 6、静态内部类
- 7、局部内部类
- 8、匿名内部类
一、类变量和类方法
独立于对象之外的变量和方法
1、类变量(静态变量)
(1)为什么需要静态变量?
在实际开发场景中,处理问题时,会需要两个类在同一内存区域中共享一个数据,或共用一个方法,此时就需要使用到静态变量和静态方法
(2)什么是静态变量?
类变量也叫静态变量,静态属性,为该类所有对象共享的变量
- 语法:
访问修饰符 static 数据类型 变量名; //(推荐)
static 访问修饰符 数据类型 变量名;
(3)怎么使用静态变量?
- 访问
类名.类变量名;(推荐)
对象名.类变量名;
(4)使用静态变量时需要注意的问题
- 使用时机:需要让某个类所有对象共享一个属性时,使用静态变量
- 类变量与实例变量的区别
- 类变量为该类所有对象共享
- 实例变量每个对象各自独享
- 加上static的变量称为类变量或静态变量,否则称为实例变量/普通变量/非静态变量
- 访问时需要满足访问权限和范围这一限制
- 实例变量不能通过类名访问
- 类变量在类加载时就创建了,即使无实例对象也能访问
- 类变量的生命周期随着类的加载而开始,随着类的消亡而结束
2、类方法(静态方法)
(1)什么是静态方法?
- 语法
访问修饰符 static 数据返回类型 方法名() {}
(推荐)static 访问修饰符 数据返回类型 方法名() {}
(2)怎么使用静态方法?
在满足访问修饰符的前提下
- 访问
类名.类方法名
对象.类方法名
(3)使用静态方法时需要注意的问题
- 类方法和普通方法随类的加载而加载,将结构信息存储在方法区
- 类方法中无this的参数
- 普通方法隐含this的参数
- 类方法可以通过类名调用,也可以通过对象调用
- 普通方法和对象有关,需要对象调用
- 类方法中不允许使用和对象有关的关键字(
this
,super
) - 类方法只能访问类变量和类方法(静态方法只能访问静态成员)
- 普通方法既可以访问静态成员,又可以访问非静态成员
- 静态属性,方法可以别继承,但不能被重写
二、main方法语法解析
public static void main(String[] args) {}
(1)理解main方法
- main方法由Java虚拟机调用
- JVM需要调用类的main方法,则该方法的访问权限必须为
public
- JVM在执行main方法时,不必创建对象,由类名直接调用,则该方法是
static
- 该方法接受String类型数组参数,该数组保存执行Java命令时传递给所运行的类的参数
String[] args
的意义:传递参数——在控制台输入:java 文件名 参数一 参数一 ...
(2)说明
- 在main方法中,可以直接调用main方法所在的类的静态方法或属性
- 但不能访问该类中非静态成员,必须实例化对象才能访问
三、代码块{}
1、什么是代码块?
(1)代码块
代码块属于类中的成员(类的一部分)
类似于方法,将逻辑语句封装在方法体中,通过{}
包围起来
但是与方法不同,代码块没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显式调用,而是加载类,或创建对象时隐式调用
(2)语法
修饰符 { 代码; };
- 修饰符可选无或static
- 逻辑语句中可以为任何逻辑语句(输入,输出,方法调用,循环,判断等)
;
可以省略
(3)三种代码块
- 普通类中方法的方法体
- 构造代码块:创建对象时被调用,优先于构造方法被执行(初始化一个类的所有构造器的共有特征)
- 静态代码块:只执行一次,用来初始化静态成员变量(随着类的加载而执行)
2、使用细节
(1)静态代码块随着类的加载而执行,且只执行一次
(2)类什么时候被加载
- 创建对象实例时
- 创建子类对象实例时,父类也会别加载
- 使用类的静态成员时,类被加载,静态代码块随着类的加载而执行
(3)构造代码块在创建实例时被隐式地调用,创建一次,调用一次
(4)代码块在创建一个对象时,在一个类中的调用顺序是
- 调用静态代码块,和静态属性的初始化(二者优先级一样,按顺序执行)
- 调用构造代码块,和普通属性的初始化(二者优先级一样,按顺序执行)
- 调用构造方法
(5)构造器前隐含了super()
构造器,和调用构造代码块,即父类构造器优先于调用本类构造代码块
(6)创建一个子类对象时,他们的静态代码块,静态属性初始化,构造代码块,普通属性初始化,构造方法的调用顺序如下:
- 父类的静态代码块和静态属性的初始化
- 子类的静态代码块和静态属性的初始化
- 父类的构造代码块和普通属性的初始化
- 父类的构造器
- 子类的构造代码块和普通属性的初始化
- 子类的构造器
(7)静态代码块只能直接调用静态成员,构造代码块可以调用任意成员
3、代码块的好处
(1)构造器的补充机制,相当于另外一种形式的构造器,可以做初始化的操作
(2)如果多个构造器中都有重复语句,可以抽取到代码块中,提高代码的重用性
四、单例设计模式(静态方法和属性的经典使用)
(1)什么是设计模式?
设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格以及解决问题的思考方式
(2)什么是单例模式?
所谓的单例模式,即单个实例,指采取一定的方法保证在整个的软件系统中只能存在一个实例,并且该类只提供一个取得其对象的方法
- 饿汉式
- 懒汉式
(3)饿汉式
- 构造器私有化——》防止用户新建实例
- 类的内部创建对象(静态属性)
- 向外暴露一个静态的公共方法——》
getInstance()
public class SingleTon01 { //通过类调用静态方法获取对象 GirlFriend instance = GirlFriend.getInstance(); } class GirlFriend { private String name; //2、在类的内部创建对象 //为了能在静态方法中使用,需要将其修饰为静态方法 private static GirlFriend gf = new GirlFriend("小花花"); //保证只有一个实例对象 //1、构造器私有化 private GirlFriend(String name) { this.name = name; } //3、提供暴露的静态公共方法,返回gf对象 public static GirlFriend getInstance() { return gf; } }
(4)懒汉式
- 构造器私有化
- 类的内部类创建对象的引用(不直接指向对象,不直接实例化)
- 提供静态公共方法,对引用进行判断,为空则实例化,return对象
public class SingleTon02 { } class Cat { private String name; //不直接实例化 private static Cat cat; private Cat(String name) { this.name = name; } //懒汉式——在静态的公共方法中进行对象的实例化 public static Cat getInstance() { if (cat == null) {//保证单例 cat = new Cat("小可爱"); } return cat; } }
(5)比较
- 二者主要区别在于创建对象的时机不同,饿汉式类加载即创建,懒汉式使用才创建
- 饿汉式五线程安全问题,懒汉式有线程安全问题
- 饿汉式存在资源浪费的可能
五、final关键字
可以用来修饰类,属性,方法和局部变量
- 基本数据类型:值不变
- 引用数据类型:对象不变
- 方法:不能重写
- 类:不能继承
1、使用场景
(1)当不希望类被继承时,用final修饰——修饰类
(2)当不希望父类的某个方法被子类重写时,用final——修饰方法
(3)当不希望类的某个属性被修改时,用final——修饰属性
(4)不希望某个局部变量被修改——修饰局部变量
2、使用细节
(1)final修饰的属性又叫做常量,一般用XX_XX_XX来命名
(2)final修i是的属性在定义时,必须赋初始值,且不能修改
-
赋值的位置
- 定义时:
public final double TAX_RATE = 0.08;
- 在构造器中可以给常量赋值
- 在代码块中给常量赋值
类的成员变量,局部变量在使用前初始化即可
- 定义时:
(3)如果final修饰的属性为静态的,则初始化位置只能在如下所示位置
-
位置
- 定义时
- 静态代码块中
类加载——》静态代码块执行
(4)final修饰类不能继承,但可以实例化对象
(5)如果不是final类,含有final方法,则该方法不能重写,但可以被继承
(6)final类中所有的方法被隐式设置为final方法
(7)final不能修饰构造方法
(8)final和static往往搭配使用,效率更高,不会导致类加载(底层做了优化)
(9)包装类(Integer,Double,Float,Boolean)都是final类,String也是final类
六、抽象类(abstract)
1、为什么需要抽象类?
父类方法的不确定性
当父类的某些方法需要声明,但又不确定该如何实现时,可以将其声明为抽象方法,那么这个类就是抽象类
- 抽象方法:即没有实现的方法
- 没有实现:没有方法体
- 抽象类:当一个方法为抽象方法时,类为抽象类
2、抽象类的介绍
抽象类除了继承毫无意义
(1)用abstract关键字来修饰一个类时,类为抽象类
语法:访问修饰符 abstract class 类名 {}
(2)抽象方法
语法:访问修饰符 abstract 返回类型 方法名(形参列表);
(无方法体)
(3)抽象类的价值在于设计,是设计者设计好后,让子类实现抽象类
(4)抽象类为考官常考知识点,子啊框架和设计模式中涉及较多
3、使用细节
(1)抽象类不能实例化
(2)抽象类不一定要有抽象方法,而且可以有实现方法
(3)有抽象方法一定是抽象类
(4)abstract只能修饰类和方法
(5)抽象类仍然是类,可以有任意类可以有的成员
(6)抽象方法不能有方法体
(7)如果一个类继承了抽象类,则必须实现(重写)抽象类的所有抽象方法,除非它也声明为abstract类
(8)抽象方法不能使用private,final和static来修饰,因为与重写向违背
- private:私有,子类无法继承父类特有的成员
- final:final方法子类不能继承
- static:静态成员只与类有关,可以继承不能重写
七、模板设计模式——抽象类的最佳实践
1、基本介绍
抽象类体现的就是一种模板模式的设计,抽象类作为子类的通用模板
子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为
2、作用
(1)当内部功能一部分实现是确定的,一部分实现是不确定的,这时可以把不确定的部分暴露出去,由子类实现
(2)编写一个抽象父类,父类提供了多个子类的通用方法,并把一个或多个方法留给其子类实现,就是一种模板模式
public class AbstractExercise01 { public static void main(String[] args) { //创建员工和经理 CommonEmployee commonEmployee = new CommonEmployee("jack", 51, 2000); Manager manager = new Manager("tony", 52, 20000, 100000); commonEmployee.work(); manager.work(); } } //抽象类 abstract class Employee { private String name; private int id; private double salary; public Employee(String name, int id, double salary) { this.name = name; this.id = id; this.salary = salary; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } public abstract void work(); } class Manager extends Employee { private double bonus;//奖金 public Manager(String name, int id, double salary, double bonus) { super(name, id, salary); this.bonus = bonus; } public double getBonus() { return bonus; } public void setBonus(double bonus) { this.bonus = bonus; } @Override public void work() { System.out.println("经理" + this.getName() + "正在工作中..."); } } class CommonEmployee extends Employee { public CommonEmployee(String name, int id, double salary) { super(name, id, salary); } @Override public void work() { System.out.println("员工" + this.getName() + "正在工作中..."); } }
八、接口(interface)
接口就是一个标准
1、为什么需要接口?
(1)接口被用来描述一种抽象(用来实现多继承)
(2)接口被用来实现抽象,抽象类也被用来实现抽象,为什么一定要用接口,二者之间的区别是什么?
- 抽象类内部可能含有非final的变量
- 但是在接口中存在的变量一定是final
- final即不可改变,不可改变即为一个标准
(3)为不同类顺利交互提供标准
2、什么是接口?
接口就是给出一些没有实现的方法,封装到一起,在某个类要使用的时候,根据具体情况,实现方法
- 语法:
//定义接口 interface 接口名 { //属性 //方法 } //实现 class 类名 implements 接口名 { //属性 //方法 //必须实现的接口的抽象方法 }
- 接口中的方法:
- 抽象方法
- 默认实现方法
- 静态方法
3、应用场景
指定标准,接口是需求的实际体现
4、注意事项
(1)接口不能被实例化
(2)接口中的所有方法是public方法,接口中抽象方法可以不用abstract修饰,因为interface中方法默认为public abstract修饰
(3)一个普通类实现接口,就必须将该接口的所有方法都实现
(4)除非声明为抽象类,上(3)可不用全部实现
(5)一个类可以实现多个接口
(6)接口中的属性 ,只能是final
,而且是public static final
修饰
(7)接口属性的访问:接口名.属性;
(8)接口不能继承类,但可以继承多个别的接口
- 类继承类
- 类实现接口
- 接口继承接口
(9)接口修饰符和类一样,只能是public和默认的
5、接口与继承
(1)解决问题不同
- 继承:解决代码复用性和可维护性
- 接口:设计,设计好各种方法的规范,由其他类实现
(2)接口比继承更灵活
- 继承:满足
is-a
关系,子类是一个父类 - 接口:满足
like-a
,类像一个接口
(3)接口在一定程度上实现代码解耦
接口规范性+动态绑定
6、接口的多态性
(1)参数的多态性
接口引用可以指向实现了接口的类的对象
(2)多态数组
public class Arr { public static void main(String[] args) { //多态数组 Usb[] usbs = new Usb[2]; usbs[0] = new Phone(); usbs[1] = new Camera(); //调用 for(int i = 0; i < usbs.length; i++) { usbs[i].work;//动态绑定 if (usbs[i] instenceof Phone) { ((Phone)usbs[i]).call();//向下转换:前提是引用指向的对象本来就是Phone类 } } } } //接口 interface Usb { void work(); } class Phone implements Usb { public void call() { "手机打电话".sout;//输出 } //实现 public void work() { "手机工作中".sout; } } class Camera implements Usb { public void work() { "相机工作中".sout; } }
(3)接口的多态传递
interface IH{} //IG 继承 IH interface IG extends IH {} //Teacher类实现IG接口的同时也实现了IH接口 class Teacher implements IG {} //主方法简写 main { IG ig = new Teacher();//一个IG接口的引用指向了一个Teacher类的实例 IH ih = new Teacher();//一个IH接口的引用指向了一个Teacher类的实例 }
九、内部类
1、为什么需要内部类?
每个内部类都能独立地实现一个接口,无论外部类是否实现了接口,对内部类而言都不影响,从而结局了Java多继承的问题
2、什么是内部类?
一个类的内部完整地嵌套了另一个类结构,被嵌套的类称为内部类,嵌套其他类的类为外部类,是类的第五大成员(属性,方法,构造器,代码块,内部类)
最大的特点:可以直接访问外部类的私有属性,并且可以体现类之间的层级关系
3、语法
class 外部类 { class 内部类 { } } class 外部其他类 { }
4、内部类的分类
-
定义在外部类成员的位置上的内部类,按静态非静态划分为
-
成员内部类
-
静态内部类
-
-
定义在外部类成员的局部位置上的内部类,按有无类名区分为
- 局部内部类
- 匿名内部类
5、成员内部类
外部类的成员位置,且非static
(1)可以直接访问外部类的所有成员,包含私有的
(2)地位为成员,则可以添加任意访问修饰符
(3)作用域:和外部其他类一样,整个类体
(4)成员内部类访问外部类:直接访问
(5)外部类访问成员内部类成员:在外部类里建立成员内部类对象,再调用
(6)外部其他类访问成员内部类成员
外部类.内部类 引用 = 外部类对象.new 内部类(实参列表);
- 外部类编写一个方法,返回一个内部类的对象实例
(7)外部类和内部类成员重名时,就近原则,若要访问外部类——外部类名.this.成员;
外部类名.this
:表示指向外部类的对象
6、静态内部类
外部类的成员位置,为静态
(1)可以访问外部类所有静态成员,不能访问非静态成员
(2)可以添加任意修饰符
(3)作用域:整个类体
(4)静态内部类直接访问外部类成员
(5)外部类访问静态内部类成员:实例化静态内部类一个对象,再调用、
静态内部类只是说明该类属于外部类,而不属于外部类实例化的对象,而静态内部类的非静态成员,依然和静态内部类的对象相联系,所以需要实例化静态内部类对象后再调用目标成员
(6)外部其他类访问静态内部类
-
外部类.静态内部类 引用 = new 外部类.静态内部类(); 引用.成员;
静态成员可以通过类名来调用,静态内部类为静态成员,ps:静态内部类的成员不一定为静态
- 在外部类编写一个方法,返回静态内部类实例对象
(7)外部类和内部类成员重名时,就近原则,若要访问外部类——外部类名.this.成员;
7、局部内部类
通常在方法中,有类名
(1)可以直接访问外部类的所有成员,包含私有的
(2)地位为一个局部变量,不能使用权限修饰符,但可以使用final关键字
(3)作用域:仅在定义它的方法或代码块中
(4)局部内部类访问外部类成员:直接访问
(5)外部类访问局部内部类:满足作用域的条件下(即在方法中),创建对象再访问
(6)外部其他类无法访问局部内部类:因为局部内部类为一个成员变量
(7)外部类和内部类成员重名时,就近原则,若要访问外部类——外部类名.this.成员;
8、匿名内部类
匿名的局部内部类,不是没名,而是系统自动分配类名
(1)语法
new 类或接口(参数列表) { 类体; }; //底层运行逻辑 class 外部类名$02 implements 类或接口 {//实现了类接口或继承了类 类体; } new 外部类名$02();//实例化一个匿名内部类的对象
//例子 public class AnonymousInnerClass {//外部其他类 public static void main(String[] args) { Outer outer = new Outer(); outer.method(); } } class Outer {//外部类 private int n1 = 10;//属性 public void method() {//外部类的方法中建立内部类 /** * 1、需求:使用A接口,并创建对象 * 2、传统方式:创建一个类重写方法,实现接口,并创建对象 * 3、若tiger类只使用一次,后面不再使用 * 4、解决方案:使用匿名内部类,来简化开发 * */ //基于接口的匿名内部类 /** * 匿名内部类基于接口的解析 * 1、tiger的编译类型为:IA接口类型 * 2、tiger的运行类型为:匿名内部类,匿名内部类并非没有名字,只是由系统分配类名,底层如下 * -IA() {类体};:创建了一个实现了接口的匿名内部类,注意为一个语句,需要分号“ ; ” * class Outer$01 implements IA { * 类体 * }; * new IA() {}; :创建了一个匿名内部类的对象 * new Outer$01(); * IA tiger = new IA() {}; * 将实例化的一个实现了A接口的匿名内部类的对象的地址传递给tiger *3、匿名内部类使用一次,就不能再使用了,但是tiger可以重复使用 * */ IA tiger = new IA() {//基于接口的匿名内部类 @Override public void cay() { System.out.println("老虎嗷嗷叫"); } }; //在外部类中,在内部类作用域即方法中,调用内部类的成员——创建一个指向内部类对象的引用,用对象来调用方法 tiger.cay(); // Tiger tiger = new Tiger(); // tiger.cay(); //基于类的匿名内部类 /** * 匿名内部类基于类的解析 * 1、father的编译类型:Father * 2、father的运行类型:Outer$2 father对象的运行类型:class com.zyhstu.innerclass.Outer$2 * -Father("jack") {类体};:创建了一个继承了Father父类的Outer$2子类 * class Outer$2 extends Father { * 类体; * }; * -new Father("jack") {类体};:创建一个匿名内部类Outer$2的对象,并将jack自动传给Father的构造器 * new Outer$2() {类体}; * -Father father = new Father("jack") {}; * 实例化一个继承了Father父类的Outer子类的对象,并将地址传递给一个Father的引用 * */ Father father = new Father("jack") { @Override public void test() { System.out.println("匿名内部类重写了test()方法"); } }; father.test(); System.out.println("father对象的运行类型:" + father.getClass()); //基于抽象类的匿名内部类 Animal animal = new Animal() { @Override void eat() { System.out.println("小狗吃骨头"); } }; animal.eat(); } } interface IA { public void cay(); } //class Tiger implements IA { // @Override // public void cay() { // System.out.println("老虎嗷嗷叫"); // } //} // //class Dog implements IA { // @Override // public void cay() { // System.out.println("小狗汪汪叫"); // } //} class Father {//外部其他类 public Father(String name) {//构造器 } public void test() {//方法 } } //抽象类 abstract class Animal { abstract void eat(); }
(2)匿名内部类的使用
-
new 类或接口(实参列表) { 类体; }.方法(方法实参);
-
类名 引用 = new 类或接口(实参列表) { 类体; } 引用.方法(方法实参);
(3)注意事项
- 可以直接访问外部类的所有成员,包含私有的
- 地位为一个局部变量,不能使用权限修饰符,但可以使用final关键字
- 作用域:仅在定义它的方法或代码块中
- 匿名内部类访问外部类成员:直接访问
- 外部其他类不能访问匿名内部类:局部变量
- 外部类和内部类成员重名时,就近原则,若要访问外部类——
外部类名.this.成员;
这篇关于12、面向对象(高级)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-12-22项目:远程温湿度检测系统
- 2024-12-21《鸿蒙HarmonyOS应用开发从入门到精通(第2版)》简介
- 2024-12-21后台管理系统开发教程:新手入门全指南
- 2024-12-21后台开发教程:新手入门及实战指南
- 2024-12-21后台综合解决方案教程:新手入门指南
- 2024-12-21接口模块封装教程:新手必备指南
- 2024-12-21请求动作封装教程:新手必看指南
- 2024-12-21RBAC的权限教程:从入门到实践
- 2024-12-21登录鉴权实战:新手入门教程
- 2024-12-21动态权限实战入门指南