OOP 面向对象程序设计 面向对象的特征 封装、继承、多态 开发的过程其实就是不断的创建对象,使用对象,指挥对象做事情。 设计的过程,其实就是在管理和维护对象之间的关系。
对象的调用:
对象名.成员变量(全局变量)名
对象名.成员方法()
内存分析
栈内存: 用来存放一些基本类型的变量和对象的引用变量。例如八大基本数据类型、对象的句柄等。
栈内存的数据的大小和生存周期都是确定的,
优点:寄存速度快、栈数据可以共享。
缺点:数据固定,不够灵活。
堆内存:
用来存放所有new 创建的对象和数组的数据
//栈
byte a =127;
boolean b = true;
//ObjectO objecto ---->对象的句柄(引用)
// 栈 堆
ObjectO objecto = new ObjectO();
// 栈 堆
int[] sum = new int[]{1, 2, 3};
封装
/**
* @program: javaobject
* @description: 电脑,,,封装大大提高了安全性。
* 封装:
* 属性私有化,如果改属性需要提供或者修改,
* 应该设置行为(方法)去间接修改,而不是直接修改。
* @author: BiMoYuanKong
* @create: 2022-05-07 16:11
**/
public class Computer {
private String cpu = "i3";
private String N = "小米品牌";
public void open(){
System.out.println("开机了");
}
public void close(){
System.out.println("关机了");
}
public void setCPU(String cpu){
//this 代表当前这个实例对象
System.out.println(this.cpu); //i3
this.cpu = cpu;
System.out.println(cpu); //i7
}
public void setN(String N){
this.N= N;
System.out.println(N); //华硕品牌
}
}
public static void main(String[] args) {
Computer computer = new Computer();
computer.setCPU("i7");
computer.setN("华硕品牌");
}
继承(extends)
继承用一句话来说就是老爸的东西就是你的,你的东西不一定是老爸的。
继承的好处:
1)简化了类的编写(避免冗余代码)
2)可以让父类、子类以及后面的孙类,形成一个体系,这个也是多态的基础
3)不支持多继承
4)private(私有)修饰的成员可以继承,但是不可见
5)构造函数不可以被继承,子类的构造函数会调用父类的构造函数
6)如果子类的成员与父类相同,子类会覆盖父类
想要构建子类,必须构建父类--->调用子类构造函数时,必须先调用父类构造函数===》也就是说如果不在子类中的构造函数调用父类的构造函数,则会默认调用父类中的默认无参构造函数。
父类(基类) 子类(派生类)
public class Father {
public Father(String name){
System.out.println("父类实例化");
}
public void look(){
System.out.println("父亲在看东西");
}
public void drink(){
System.out.println("父亲在喝酒");
}
}
public class Children extends Father {
public Children(){
super("李华");
System.out.println("子类实例化");
}
public void drink(){
System.out.println("儿子在喝酒");
}
public static void main(String[] args) {
Children children = new Children();
children.look();
children.drink();
}
}
继承调用
//左引用(决定调用的方法是谁) 右实例(决定使用的方法是谁)
//继承链中对象方法的调用存在一个优先级:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。
super 关键字
主要存在于子类方法中,用于指向子类对象中父类对象。 访问父类的属性、函数、构造函数。
多态
多态是同一个行为具有多个不同表现形式或形态的能力。
多态存在的三个必要条件
- 继承
- 重写
- 父类引用指向子类对象
对象的多种状态是指子类重新定义父类的虚方法。当子类重新定义了父类的虚方法后,父类根据赋给它的不同的子类,动态调用属于子类的该方法。
public class Human {
String name;
public void eat(){
System.out.println("在吃东西");
}
}
public class Doctor extends Human{
String name = "医生";
public void eat(){
System.out.println(name + "在吃医保");
}
}
public class Teacher extends Human {
String name = "老师";
public void eat(){
super.eat();
System.out.println(name + "在吃铁饭碗");
}
}
public class Test {
public static void main(String[] args) {
Human d = new Doctor();
Human t = new Teacher();
d.eat();//医生在吃医保
t.eat();// 在吃东西
//老师在吃铁饭碗
}
}
重写和重载
重载: 同一个类中,方法名相同,参数数量或者类型不同,与返回值无关。
重写:是在子类中,除了方法体和访问修饰符(有一定规则),其他内容必须和父类一样。
instanof
instanceof 判断某个实例是否来源于某个类, 一般用于有继承关系或者实现等等来源问题的判断。
类的实例包含本身的实例以及所有直接或者间接子类的实例
用法:
对象 instanceof 类;
一个编译单元(Java文件)可以存在多个类,在编译时产生多个不同的.class文件,.class文件时程序运行的数据来源。Java将public类作为每个编译单元的数据接口, 只能有一个,不然不能处理存在多个类的Java文件。当一个编译单元(Java文件)有多个非public类时,运行时需要对数据来源进行选择。 一个类里面,只有一个公开类作为入口。在编译时,其他非公开会根据这个入口去生成对应的.class
Object
Object类是所有Java类的祖先,所有对象(包括数组)都能实现这个类的方法。在不明确给出超(super)类的情况下,Java会自动把Object作为要定义类的超类,所以每个类默认都有如下方法:
HuaShuoComputer h1 = new HuaShuoComputer();
HuaShuoComputer h2 = new HuaShuoComputer();
Computer computer = new Computer();
//instanceof 判断某个实例是否来源于某个类 一般用于有继承关系或者实现等等来源问题的判断
System.out.println(h1 instanceof Computer); //true
System.out.println(computer instanceof Computer); //true
System.out.println(computer instanceof HuaShuoComputer); //false
//类是引用类型
System.out.println(computer instanceof Object); //true
//equals 这个是用来判断两个对象是否一致,但是字符串的时候,是比较值
System.out.println(h1.equals(h2)); //false
System.out.println(h1);
System.out.println(h2);
String str = new String("123456");
String str2 = new String("123456");
System.out.println(str.equals(str2)); //true
System.out.println(str == str2); //false
final
final可以修饰类、函数、属性
修饰的局部变量 ---> 常量 修饰类的函数 ---> 不能够被重写 修饰的类 ---> 不能够被继承
访问修饰符
//public (公开的) 所有的类都可以访问
//protected(安全的) 不同包的普通类不能访问,但是子类可以访问
//default (缺省) 不同包不能访问,同包所有类都可以访问
//private 只有本类可以访问
方法(也叫函数)
什么是方法?
Java方法是语句的集合,它们在一起执行一个功能。
- 方法是解决一类问题的步骤的有序组合
- 方法包含于类或对象中
- 方法在程序中被创建,在其他地方被引用
- 设计原则:一个方法只完成一个功能,保持方法的原子性。
举个例子:
分析: System.out.println();
println() 是一个输出且换行方法。
System 是系统类。
out 是标准输出对象。
创建方法
创建一个方法包含以下语法:
修饰符 返回值类型 方法名(参数类型 参数名){
...
方法体
...
return 返回值;
}
特点
定义函数可以将功能代码进行封装。
便于对该功能能的复用并且只有被调用的时候才会被执行。
提高了代码的复用性
方法调用
调用方法有两种方式,即方法有返回值或无返回任何值
void关键字允许创建不返回值的方法
public static void main(String[] args) {
Scanner myInput = new Scanner(System.in);
System.out.println("请你输入第一个数:");
int x = myInput.nextInt();
System.out.println("请你输入第二个数:");
int y = myInput.nextInt();
int max = getMaxNumber(x, y);
System.out.println("两者之间最大的数是:" + max);
myInput.close();
}
public static int getMaxNumber(int a, int b) {
// int max = a > b ? a : b ;
int max = Math.max(a, b);
return max;
}
可变参数
JDK 1.5 开始,Java支持传递同类型的可变参数给一个方法。
方法的可变参数的声明如下所示:
typeName... parameterName在方法声明中,在指定参数类型后加一个省略号(...) 。
一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。任何普通的参数必须在它之前声明。
public class AddDemo {
public static void main(String args[]) {
// 调用可变参数的方法
printSum(34, 3, 3, 2, 56.5);
printSum(new double[]{1, 2, 3});
}
public static void printSum( double... numbers) {
if (numbers.length == 0) {
System.out.println("No argument passed");
return;
}
double result = numbers[0];
for (int i = 1; i < numbers.length; i++){
result += numbers[i];
}
System.out.println("The sum is " + result);
}
}
finalize() 方法
Java 允许定义这样的方法,它在对象被垃圾收集器析构(回收)之前调用,这个方法叫做 finalize( ),它用来清除回收对象。
例如,你可以使用 finalize() 来确保一个对象打开的文件被关闭了。
在 finalize() 方法里,你必须指定在对象销毁时候要执行的操作。
finalize() 一般格式是:
protected void finalize() { // 在这里终结代码 }
关键字 protected 是一个限定符,它确保 finalize() 方法不会被该类以外的代码调用。
当然,Java 的内存回收可以由 JVM 来自动完成。如果你手动使用,则可以使用上面的方法。
public class FinalizationDemo {
public static void main(String[] args) {
Cake c1 = new Cake(1);
Cake c2 = new Cake(2);
Cake c3 = new Cake(3);
c2 = c3 = null;
System.gc(); //调用Java垃圾收集器
}
}
class Cake extends Object {
private int id;
public Cake(int id) {
this.id = id;
System.out.println("Cake Object " + id + "is created");
}
protected void finalize() throws java.lang.Throwable {
super.finalize();
System.out.println("Cake Object " + id + "is disposed");
}
}
在实际开发中不推荐使用 finalize 方法,它虽然被创造出来,但无法保证 finalize 方法一定会被执行,所以不要依赖它释放任何资源,因为它的执行极不“稳定”。在 JDK 9 中将它废弃,也很好的证明了此观点。
因为 finalize 的执行是和垃圾收集关联在一起的,一旦实现了非空的 finalize 方法,就会导致相应对象回收呈现数量级上的变慢,有人专门做过 benchmark,大概是 40~50 倍的下降。 因为 finalize 被设计成在对象被垃圾收集前调用,这就意味着实现了 finalize 方法的对象是个“特殊公民”,JVM 要对它进行额外处理。finalize 本质上成为了快速回收的阻碍者,可能导致你的对象经过多个垃圾收集周期才能被回收。
构造方法(函数/器)
构造器也叫构造方法、构造函数,是java中一种特殊的函数。函数名与相同,无返回值。
够着函数要实例化之后才能跑起来,构造函数也是函数的一种,同样具备函数的重载(Overloding)特性。
package javastudy;
public class ConfunDemo {
public static void main(String[] args) {
Confun c1=new Confun(); //输出Hello World。new对象一建立,就会调用对应的构造函数Confun(),并执行其中的println语句。
}
}
class Confun{
Confun(){ //定义构造函数,输出Hello World
System.out.println("Hellow World");
}
}
一个对象建立后,构造函数只运行一次。如果想给对象的值再赋新的值,就要使用set和get方法,此时是当做一般函数使用
public class QQ {
private String username;
private String password;
private String address;
//构造方法,跟类名一致,构造函数无需声明返回值
//构造方法默认不声明的情况下,有一个自带无参构造方法
public QQ(String username, String password, String address){
this.username = username;
this.password = password;
this.address = address;
System.out.println("3个参数,我被执行了");
}
public QQ(String username, String password){
this.username = username;
this.password = password;
System.out.println("2个参数,我被执行了");
}
public QQ(){
System.out.println("无任何参数,我被执行了");
}
public static void main(String[] args) {
QQ qq = new QQ("李华","123456");
System.out.println(qq.username);
}
}
默认构造方法
当一个类中没有定义构造方法时,系统会给该类中加一个默认的空参数的构造函数,方便该类初始化。只是该空构造函数是隐藏不见的。
如下,Person(){}这个默认构造函数是隐藏不显示的。
class Person {
//Person(){} }
当在该类中自定义了构造函数,默认构造函数就没有了。如果仍要构造函数,则需要自己在类中手动添加。
重载(Overload)
重载是一个类中多态性的一种表现。 重载增强了方法的可阅读性,优化了程序设计。
调用方法时通过传递给它们的不同参数个数和参数类型来决定具体使用哪个方法, 这就是多态性。
重载 :函数名必须相同,必须具有不同的参数列表(也就是参数的个数或者类型不一致);
public class FunctionOverloading {
public static void main(String[] args) {
System.out.println(add(10,20));
System.out.println(add(10.10,10.23));
System.out.println(add(10,2,20));
}
public static int add(int a, int b){
return a + b;
}
public static double add(double a, double b){
return a + b;
}
public static int add(int a, int b, int c){
return a + b + c;
}
}
方法重写(Override)
重写:需要有继承关系,子类重写父类的方法。 1、方法名必须相同 2、参数列表必须相同 3、返回的类型必须一直与被重写的方法的返回类型相同 4、修饰符:范围可以扩大但不能缩小: public > protected > default > private 5、抛出的异常:范围可以被缩小,但不能扩大。 ClassNotFoundException -->Exception(大)
重写:子类的方法和父类必须要一致,方法体不同
为什么需要重写: 1、对于父类的功能,子类不一定需要,或者不一定瞒住。 Alt + Insert : override;
重写与重载直接的区别
重写与重载都对方法里面的内容没有强制性要求.
巧记重写: 老师过来跟你说:抄个英语单词都能抄错,赶紧重新抄写一遍.这里面的英语单词可以理解为方法名和参数列表. 而在抄写单词的过程中你不能够保证(概率问题)自己抄写的单词每次位置都和上次抄写的一模一样,这就是方法里面的内容可以不一样.
简明了解
重载反映的是"随机应变". 同样一项功能, 根据数据类型的不同, 采用不同的处理方式. 比如, 同样是吃饭, 去高档餐厅吃西餐, 你会西装革履, 但是去吃火锅, 穿便装就比较合适.
重写反映的是"父子差异". 你"继承"了父亲吃火锅的爱好, 但是吃同一份鸳鸯锅(注意, 数据类型相同) , 你喜欢涮红汤, 你父亲喜欢涮清汤
总结(硬)
方法的重写(Overriding)和重载(Overloading)是java多态性的不同表现,重写是父类与子类之间多态性的一种表现,重载可以理解成多态的具体表现形式。
(1)方法重载是一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同,则称为方法的重载(Overload)。
(2)方法重写是在子类存在方法与父类的方法的名字相同,而且参数的个数与类型一样,返回值也一样的方法,就称为重写(Overrie)。
(3)方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。
public String toString()函数的使用
public String toString()是object的方法,任何类都是Object 的子类,故都继承了其方法;
public String toString(); 这个函数能主动调用 toString 库函数,用toString函数产生的字符串来输出要输出的东西.