一、定义
使用步骤:
1.定义类
2.创建对象
3.调用类的属性和方法
(一)定义类
1.java
一个java文件中可以定义多个class类,且只能一个类是public修饰,而且public修饰的类名必须成为代码文件名。
实际开发中建议还是一个文件定义一个class类。
(1)语法
class 类名{1.成员变量(代表属性,一般是名词);2.成员方法(代表行为,一般是动词);3.构造器(后面学习);4.代码块(后面学习);5.内部类(后面学习);
}
类名前可以public、private、protected等修饰词修饰,但不是必不可少。这些名词的含义见后续总结。
(2)示例
package Day7_Object;public class L01_GirlFrend {// 1.定义属性String name;String address;int age;int height;double weight;// 2.方法public void Cooke() {System.out.println("会做饭~~");}public void Sing() {System.out.println("会唱歌~~");}public void Dance() {System.out.println("会跳舞~~");}public void Draw() {System.out.println("会画画~~");}public void Shopping() {System.out.println("会购物~~");}
}
2.python
(二)类属性
1.java
成员变量(类属性)的完整定义格式是:修饰符 数据类型 变量名称=初始化值; 一般无需指定初始化值,存在默认值。一般创建对象后赋值。
package Summarize;public class Property {public static void main(String[] args) {// 1.用类名调用类属性(定义成员属性前需要加static修饰词)System.out.println(Phone.brand);System.out.println(Phone.price);// 2.用对象名调用类属性(定义对象属性时,属性前不可加static,否则无法获取)Radio r = new Radio("德生",18);System.out.println(r.name);System.out.println(r.width);;}}
class Phone{// 属性static String brand="华为";static int price=1819;// 方法public void call() {System.out.println("打电话~~");}public void PlayGame() {System.out.println("玩游戏~~");}
}
class Radio{String name;int width;public Radio(String name, int width) {this.name = name;this.width = width;}public void broadcast() {System.out.println("播放广播~~");}
}
2.python
class Student():School="中楼小学"Headmaster="王廷新" # 校长def study(self):print("学习知识~~")def play(self):print("课间嬉戏~~")
s1=Student()
print(s1.School)
print(s1.Headmaster)
print(Student.School)
print(Student.Headmaster)
s1.study()
s1.play()
(三)类方法
(四)构造方法(又称为构造函数)
1.java
(1)构造方法
构造方法也叫作构造器、构造函数。
作用:在创建对象的时候给成员变量进行赋值的。
(2)示例代码:
package Summarize;public class L04_02 {public static void main(String[] args) {Student s1 = new Student("柯伊诗",18);s1.info();}
}
class Student{String name;int age;// 1.空参构造public Student() {}// 2.有参构造public Student(String name, int age) {this.name = name;this.age = age;}public void info() {System.out.println("学生信息\n"+"姓名:"+this.name+"年龄:"+this.age+'岁');}}
(3快捷生成构造方法
第1步: 在IDEA中按Alf+Fn+Insert,在弹出如下窗口中选择Constructor;
第2步:弹出如下窗口
第3步:点击Select None,则会生成一个空参构造,如下图所示。
第4步:重复第1步和第2步,在弹出的窗口中按Ctrl+A后如下图。
第5步:点击OK,生成一个带有全部参数的构造方法如下图。
2.python
python中的__init__(self)就是构造函数,其作用就是用来初始化对象,作用类似java中的构造方法。
关于__init__(self),讲得好的视频链接如下:【Python类中 __init__ 的通俗解释是什么?】https://www.bilibili.com/video/BV1JL411P7Wq?vd_source=4786f682d55e0415f381ab2f415da999
通过在__init__()中设置参数,控制创建对象时是否传递对象属性。
(1)空参构造
class Student():def __init__(self):passdef info(self):print(f"学生信息")
s=Student()
s.info()
(2)有参构造
class Student():def __init__(self,name,age):self.name=nameself.age=agedef info(self):print(f"学生信息\n姓名:{self.name},年龄:{self.age}")
s=Student("柯伊诗",18)
s.info()
(五)私有属性和方法
1.java
(1)特点
1)私有属性的特点
a.private是一个权限修饰符
b.可以修饰成员(成员变量和成员方法)
c.被private修饰的成员只能在本类中才能访问
package Summarize;public class L04_03 {public static void main(String[] args) {Fruit f = new Fruit("香蕉",3);f.info();//在类外面访问无法访问私有属性和方法,如果强制访问则会报错//f.name;//f.color;}
}
class Fruit{private String name;private int diameter;//直径public void info() {System.out.println("名称:"+name+",果径:"+diameter);}private void color() {System.out.println("水果的颜色:红色~~");}public Fruit(String name, int diameter) {this.name = name;this.diameter = diameter;}
}
(2)获取和修改私有属性
由于被private修饰的成员只能在本类中才能访问,所有同样可以使用仿照python,可以在类里面定义两个方法来获取和修改。通学这两个方法的命名为get__xx,set__xx,当然使用其他名称也可以。
package Summarize;public class L04_03 {public static void main(String[] args) {Fruit f = new Fruit("香蕉",3);f.info();System.out.println(f.getName());System.out.println(f.getDiameter());}
}
class Fruit{private String name;private int diameter;//直径public void info() {System.out.println("名称:"+name+",果径:"+diameter);}private void color() {System.out.println("水果的颜色:红色~~");}public Fruit(String name, int diameter) {this.name = name;this.diameter = diameter;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getDiameter() {return diameter;}public void setDiameter(int diameter) {this.diameter = diameter;}
}
(3)快速生成set和get方法
该方法可以使用IDEA,使用快捷键Alf+Fn+Insert快速生成set__xx和get_xx方法。
步骤:
第1步:在IDEA中按Alf+Fn+Insert,弹出如下窗口;
第2步:选择Getter and Setter
第3步:弹出如下窗口
第4步:按Ctrl+A全选
第5步:点击OK,即可生成set_xx和get_xx方法,生成的结果如下图
2.python
(1)私有属性的特点
a.设置私有权限的方法:在属性名和方法名 前面 加上两个下划线__。
b.私有属性和私有方法只能在类里面访问和修改。
c.在类外面访问和修改私有属性和方法会报错。
d.私有方法不能被继承。
class Father():def __init__(self,name,age):self.name=nameself.age=ageself.__money=1000def info(self):print(self.name)print(self.age)def __GirlFrend(self):print("找女朋友~~")def skill(self):self.__GirlFrend()print("钓鱼~~")def fight(self):print("打仗~~")def get__money(self):return self.__moneydef set__money(self):self.__money=2000class Son(Father):pass
f1=Father("朱元璋",28)
f1.info()
f1.set__money()
print(f1.get__money())
f1.fight()# 不能在类外面访问对象的私有属性和方法
# print(f1.__money)
# f1.__GirlFrend()s1=Son("朱棣",2)
s1.info()
s1.fight()
# 子类无法继承父类的私有属性和方法
# print(s1.__money)
# s1.__GirlFrend()
(2)获取和修改私有属性
由于私有属性和私有方法只能在类里面访问和修改,因此可以在类里面定义两个方法来获取和修改。通学这两个方法的命名为get__xx,set__xx,当然使用其他名称也可以。
class Father():def __init__(self,name,age):self.name=nameself.age=ageself.__money=1000def info(self):print(self.name)print(self.age)def __GirlFrend(self):print("找女朋友~~")def skill(self):self.__GirlFrend()print("钓鱼~~")def fight(self):print("打仗~~")def get__money(self):return self.__moneydef set__money(self):self.__money=2000class Son(Father):pass
f1=Father("朱元璋",28)
f1.info()
f1.fight()
f1.set__money()
print(f1.get__money())
(六)this和self
java中的关键字this和python中的self用法差不多。
1.java
(1)成员变量和局部变量,前者在类中生效,后者只在方法内部生效。java中同名变量的调用原则:谁离我近,我就用谁。
下面示例代码中info方法中参数name及其中的age变量,均为局部变量。
package Summarize;public class L04_05 {public static void main(String[] args) {GirlFrend g = new GirlFrend();g.info("张三");}
}
class GirlFrend{String name;private int age;public void info(String name) {int age=10;System.out.println("姓名:"+name+",年龄:"+age);}}
(2)this的作用
a.有点类似python中的self,指调用对象本身;
b.可以用来区分成员变量和局部变量;
c.this:理解为一个变量,表示当前方法调用者的地址值(黑马老师阿玮讲解,未理解,感觉python应有类似机制,查资料完善这块知识点);
d.访问成员变更、成员方法、构造方法;
e.调用的成员变量前默认有一个this;
f.static修改的成员方法不可以使用this,因非静态的东西跟对象有关,所以不能使用this,否则会报错。
2.python
python中self是指调用某类属性和方法的对象。
下述图片来源python黑马笔记。
二、封装
(一)java
1.什么是封装?
概念:对象代表什么,就得封装对应的数据,并提供数据对应的行为。
告诉我们,如何正确设计对象的属性和方法。
原则:对象代表什么,就得封装对应的数据,并提供数据对应的行为
2.理解封装思想的好处?
让编程变得很简单,有什么事,找对象,调方法就行。
降低我们的学习成本,可以少学、少记,或者说压根不用学,不用记对象有哪些
方法,有需要时去找就行
3.封装的概念(其他资料)
封装是指将对象的状态(属性)和行为(方法)包装在一起,形成一个独立的单元,并通过访问修饰符来控制对这些属性和方法的访问权限。
封装的主要目的是:
(1)数据隐藏:将对象的内部实现细节隐藏起来,只暴露必要的接口,防止外部直接访问和修改对象的内部状态,提高代码的安全性和可维护性。
(2)提高内聚性:将相关的数据和行为封装在一起,使对象的功能更加明确和集中。
(3)增强对象的稳定性:通过合理的封装,可以更好地控制对象的状态变化,避免不恰当的操作导致对象的不稳定。
4.标准javabean类
(二)python
1.什么是封装?
封装将属性和方法书写到类的里面的操作即为封装。
2.封装的作用
装封装可以为属性和方法添加私有权限
三、继承
(一)概念
1.java
什么是继承、继承的好处?
继承是面向对象三大特征之一,可以让类跟类之间产生子父的关系。
可以把多个子类中重复的代码抽取到父类中,子类可以直接使用,减少代码冗余,提高代码的复用性。
2.python
Python面向对象的继承指的是多个类之间的所属关系,即子类默认继承父类的所有属性和方法。
在Python中,所有类默认继承object类,object类是顶级类或基类;其他子类叫做派生类。
(二)语法
java
特点:java不支持多继承,但是支持多层继承。
class 子类 extends 父类 {}
python
特点:python支持多继承,同时支持多层继承。
class 子类(父类1,父类2):
示例:
package Summarize;public class L04_06 {public static void main(String[] args) {Youth Tom = new Youth();Tom.drive();Tom.play();}
}
class Father{public void drive() {System.out.println("开车");}
}
class Youth extends Father{public void play() {System.out.println("玩游戏");}
}
示例:
class Father():def Drive(self):print("开车")
class Mather():def Cook(self):print("做饭")
class Teather():def Writinge(self):print("写字")
class Youth(Father,Mather,Teather):# 青年def Play(self):print("玩游戏")
Tom=Youth()
Tom.Drive()
Tom.Cook()
Tom.Writinge()
Tom.Play()
(三)调用父类同名方法
1.java
2.python
子类和父类具有同名属性和方法,默认使用子类的同名属性和方法。注意:使用super() 可以自动查找父类。调用顺序遵循mro类属性的顺序。比较适合单继承使用。
(1)mro
在python里面,一个class,如何从他的父类里找应先使用哪个父类的函数,这个顺序叫mro。mro英文全称method resolution order,含义是方法解释顺序。
获取mro的方法
方法1:类名.mro() # 返回一个列表
方法2:类名.__mro__ # 返回一个元组
mro排序的底层机制比较复杂。如果项目的继承结构比较复杂,一定要设计出清晰继承结构,否则可能出现继承顺序混乱,从而导致出错。
本知识点资料来源:https://www.bilibili.com/video/BV1V5411S7dY?vd_source=4786f682d55e0415f381ab2f415da999
(2)supper
关于supper有一个讲得非常好的视频,链接:【【python】B站最细致的super()详解,一定有你不知道的知识!】https://www.bilibili.com/video/BV1FL4y1E7xK?vd_source=4786f682d55e0415f381ab2f415da999
supper()中初始化方法为:__init__(self, type1=None, type2=None),说明创建supper类对象需要传入两个参数:type1 类名,决定了在mro链上从哪个class开始往后找tpye2 对象,决定了这个函数bind哪个object或class上,决定使用哪个mrosuper().__init__(name) 等价于 super(Male,self).__init__(name)super(Male,self).__init__(name)中Male 是一个classself 是一个objectsupper(Male,self)的作用:首先,他要从self这个object中拿到mro,其mro为Male、Person、Object。然后,他找第一个argument(参数)也就是Male在mro里所处的位置。接着他会从Male后面的那个class开始找,那他第一个找到的就是Person。然后他就看Peron中有没有__init__这个函数,发现有__init__这个函数。然后他再把这个__init__函数bind(捆绑)到self上。bind的过程,可以理解为这个Peron的__init__函数传进去的self就是supper里的self。也就是说左边的这行代码:Person.__init__(self,name)他相当于call(召唤)一个Person.__init__(self,name)那我们为什么不直接使用Person.__init__(self,name)呢有以下几个原因:你未来有可能改变基类的名字,甚至会改变继承的方式,比如未来我们把class Person改名了,叫class HomoSapiens,那在这种情况下,如果我用 super 的话我就什么都不用管,因为他会自动追随着这个mro找到正确的class,但是如果我用这种命名的class话,我就要全部都修改。这样更容易引入错误,那当然这一点你可能能自己领悟到,或者听很多教程说过。但是还有一点是教程几乎不会说,一般人也想不到的,就是 super 这件事他其实是动态的,他会根据elf的mro进行寻找,而self也就是传进来的这个argument,他本身是动态的。也就是说,同样一个函数里面,我用super在不改变这个函数的情况下,我有可能会拿到不同的class。那这个问题呢,等等我们细讲啊细讲。那我们先说回这个super本身,我们现在又给他增加了一层继承的层级。我们说这个Person呢也是一个Animal,然后所有的Animal都有一个年龄age。这个时候,如果我们在第15行这里用super(Male,self),那他就会正常的initialize(初始化)所有的东西,他会call这个Person的__init__,然后 person 的__init__会call这个Animal的__init__,最后就完成了这个 Male。那如果我们把第15行改成super(Person,self),会有什么结果呢?大家可以看他报错了。他说这个__init__函数啊,只要两个positional arguments。这是为什么呢?因为当我们使用super(Person,self)的时候,self的mro链是Male、Person、Animal、然后object。那第一个argument它由于是Person,所以他会从Person后面的那个class,也就是Animal开始找,那Animal是有__init__函数的,但是Animal的init函数只take(带)一个age,所以当我们传入age name的时候,他就错了。那为了实验一下啊,我们可以只传进去这个age,可以看到呢,我们就初始化了一个只有age和gender的Male object,因为我们跳过了 Person的init_函数。那举这个例子呢,还是想跟大家说明super的这两个argument,也就是第一个这个 type,和第二个这个 type 或者 object,分别决定了什么。第二个是决定使用这个函数的对象和 mro,而第一个只决定了在 mro 这个链上从哪开始找。那当然在我们举的例子里面,这第二个 argument 是一个object对吧?他也可以是一个 class。那么这种形式呢,就是在class methods的时候会比较有用,这个未来我们有机会再展开讲吧。好那 super 的完全体形态,大家一定要记住,因为你只有理解了他的完全体形态,你才会在任何时刻都知道他到底在干什么。比如说你现在就可以知道,如果我加上第19行这段代码,我就可以把这个m object给完整的做初始化了.你会发现 super 这个东西,并不是只能在 class 里面使用的,他可以在任何一个地方使用。我只要给定第二个参数 object 或者 class,再给定第一个参数从哪开始找,我就能使用他的函数啊.那这里呢,我们就是说从m这个object 的mro 上,寻找 Male 后面开始的_init_函数,这样我们实际上就找到了Person的__init__函数,然后再用 Person的__init__函数,对 m 这个 object 做 initialization,大家可以看这个结果,就跟我们之前正确的那个initialization 的结果一样了.好那做一个小调查.在看这期视频之前,你知道 super 可以在 class 之外使用吗?好,那在了解了super的完全体之后呢,我们继续往下讲.super 的两个参数都是可选参数,那当你只给super一个参数的时候呢,他返回的是一个unbound object.你需要把它 bind 到一个别的 object 上才能正确的使用.那这个用法呢,相对罕见,而且一般是要比较进阶的编程的时候才会用得到,在这里呢我们就先不深入的聊了.也是未来我们讲 bound method 的时候,讲 descriptor时候,然后我们再去聊他.那我们平时接触到的最常见的用法,实际上就是这个super括号里面什么都不给.这种super的用法,是必须要在 class 的 definition(定义)里面进行的。为什么呢?因为这个super用一些python的黑魔法做了两件事。第一他会寻找自己是在哪个 class 里面被定义的,然后把这个 class放到他的第一个参数里,第二他会寻找自己是在哪个函数里被定义的,然后把这个函数的第一个 argument,放到他第二个参数里。那在当前这种情况下呢,super()就等价于 super(Male,self)。因为他是在 Male 这个 class 里被定义的,__init__这个函数第一个 argument 是 self,那今天由于时间关系呢.我们就不去分析他背后的黑魔法了,大家只要记住这个关系就可以了.他的完全体是什么,当他没有参数的时候,是从哪找到的这两个参数.好那我们之前留了一个扣(什么是扣,看能不能结合上下文弄明白什么含义)啊,我们说 super 这个东西呢。由于python dynamic(动态) 的特性,同样的函数定义,在针对不同的 object 的时候,会产生不同的效果。我们在这里呢举个例子。我们看左边这边代码非常的简单.class A 呢有一个函数是 say,然后就是打印 A,然后 B 继承了 A,B 的 say 函数呢就是用 super().say(),然后 M 继承了 B,M 的 say 函数呢也是 super().say(),那我们建立了一个 m 的 object,然后用 m.say().那这里就非常直观对不对.m 找到了 B 的 say,然后 B 再找到了 A 的say,最后打印了一个 A,我们可以看到,结果和我们的预期是一致的,没有任何惊喜啊.好呢现在我们引入一个新的 class C,我们看在 MBA 完全没有改变的情况下,我们引入了一个新的 class C,这个C 继承 A,这个 C 也有一个 say 函数,然后这个 say 函数 print("C”).我们让我们的class M在继承B的同时,还继承 C,那根据我们在之前视频学到的知识,现在 M 的 mro 应该是(M)BCA 对不对,也就是当M调用super().say()的时候,调用的应该是 class B 的这个 say 函数,请问打印结果是什么?你猜到他会是C了吗?我们甚至可以把第15行直接改成 B.say(self),显式地调用他的基类B的say函数.可以看到结果依然是打印的C.你就会发现一个非常神奇的事情了,就是 B 这个 class 本身跟 C 一毛钱关系都没有,他继承的是 A,然而在这个 class 里面用super,却有可能找到的是C的函数,这就是一个比较反直觉的事情。那他的原因我们也提到过对不对?因为第7行的本质相当于是 super(B,self),而寻找方法的顺序是由 self 的 mro 决定的,这个 self 是一个 M object,因此在寻找的时候,B 后面紧跟着的是C 而不是 A,这也就出现了在 class B 里面使用 super,调用的却不是一个 class 的父类的函数,这种奇怪的情况,那讲到这里,是不是很多人发现自己对 super 的理解,实际上是有偏差的,绝大部分人都会把 super 理解为调用父类的同名函数,其实他并不是这么简单啊,其实他并不是这么简单啊。好那这期视频就到这里,希望对大家有所帮助。
(四)重写
1.java
2.python
子类重写父类同名方法和属性。