0710
面向对象
- 面向对象基本概念
面向对象的思想,体现的是人所关注对象的信息聚集在了一个具体的物体上。人们就是通过对象的属性和行为来了解对象。
本节主要围绕Java类和Java对象展开。
对象一定是一个具体的、确定的物体。
类
类的定义:
1.类是相同或相似对象的一种抽象,是对象的一个模板,它描述一类对象的行为和状态。
2.类是具有相同属性和方法(行为)的对象的集合。
属性是对象具有的特征。
定义一个类,主要有三个步骤:
1、定义类名,用于区分不同的类。如下。class是声明类的关键字,类名后面跟上大括号,大括号里面就是类的一些信息。
public为权限修饰符。
public class 类名{
//定义属性部分(成员变量)
属性1的类型 属性1;
属性2的类型 属性2;
...
//定义方法部分
方法1;
方法2;
...
}
2、编写类的属性。对象有什么,需要通过属性来表示。属性的定义是写在类名后面的大括号里,在定义属性时,要明确属性的类型。在一个类当中可以写一个或多个属性。当然也可以不定义属性。
3、编写类的方法。方法也是写在大括号里面。可以定义一个方法或多个方法,当然也可以不定义方法。
一个类可以包含以下类型变量:
- 局部变量:在方法、构造方法或者语句块中定义的变量被称为局部变量。变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁。
- 成员变量:成员变量是定义在类中,方法体之外的变量。这种变量在创建对象的时候实例化。成员变量可以被类中方法、构造方法和特定类的语句块访问。
- 类变量:也叫静态变量,类变量也声明在类中,方法体之外,但必须声明为static类型。
对象
1.创建对象的语法
类名 对象名 = new 类名();
比如对People这个类,我想实例化Lilei这个人。LiLei的数据类型便是People这个类型。(类可以看成是我们自己定义的数据类型)
People LiLei = new People();
定义类的时候不会为类开辟内存空间,但是一旦创建了对象,系统就会在内存中为对象开辟一块空间,用来存放对象的属性值和方法。
例:
(0710第1)
(0710第2)
(0707第3)
2.创建对象后,我们就要使用对象了,使用对象无非就是对属性和方法进行操作和调用。语法如下
//引用对象属性
对象名.属性
//引用对象方法
对象名.方法
例如对LiLei的身高赋值,并调用哭这个方法
LiLei.height = 170;
LiLei.cry();
在使用时注意,成员变量可以被本类的所有方法以及与本类有关的其他类所使用。而局部变量只能在当前的方法中使用。
3.作用域
作用域可以简单地理解为变量的生存期或者作用范围,也就是变量从定义开始到什么时候消亡。
(1)局部变量的作用域仅限于定义它的方法内。而成员变量的作用域在整个类内部都是可见的。
(2)同时在相同的方法中,不能有同名的局部变量;在不同的方法中,可以有同名的局部变量。
(3)成员变量和局部变量同名时,局部变量具有更高的优先级。
构造方法
每个类都有构造方法,在创建该类的对象的时候他们将被调用,如果没有定义构造方法,Java编译器会提供一个默认构造方法。比如在新建一个对象new Object(),括号中没有任何参数,代表调用一个无参构造方法(默认构造方法就是一个无参构造方法)。构造方法的名称必须与类名相同,一个类可以定义多个构造方法。
构造方法的具体内容:
1、构造方法的名称与类名相同,且没有返回值。它的语法格式如下:
//与类同名,可以指定参数,没有返回值
public 构造方法名(){
//初始化代码
}
下面是一个构造方法的例子:
public class People{
//无参构造方法
public People(){
}
//有一个参数的构造方法
public People(int age){
}
}
又例如具体的构造方法:
public class People{
//属性(成员变量)有什么
double height;
int age;
int sex;
//构造函数,初始化了所有属性
public People(double h,int a,int s){
height = h;
age = a;
sex = s;
}
}
//创建对象,调用我们自己定义的有参构造方法
People XiaoMing = new People(168,21,1);
上面的例子中通过new关键字将类实例化成对象,而new后面跟的就是构造方法。于是可以知道new + 构造方法可以创建一个新的对象。
2、如果在定义类的时候没有写构造方法,系统会默认生成一个无参构造方法,这个构造方法什么也不会做。
3、当有指定的构造方法时,系统都不会再添加无参构造方法了。
4、构造方法的重载:方法名相同,但参数不同的多个方法,调用时会自动根据不同的参数选择相应的方法。
引用与对象实例
在新建对象实例时,需要为对象实例设置一个对象名,就像这样
Object object = new Object();
这里就像C语言的指针一样,变量object保存的其实是Object对象的引用,指向了Object对象。
例子:
Object object1=new Object();
Object object2=object1;
System.out.println(object1==object2);
运行得到的结果为true,说明object1和object2的地址相同,它们实际引用同一对象,如果改变object1对象内部的属性,那么object2的属性同样会改变。
static
静态成员
Java中被static修饰的成员称为静态成员或类成员。它属于整个类所有,而不是某个对象所有,即被类的所有对象所共享。静态成员可以使用类名直接访问,也可以使用对象名进行访问。
如:
public class StaticTest{
public static String string="Java is the best language in 砸瓦鲁多";
public static void main(String[] args){
//静态成员不需要实例化 直接就可以访问
System.out.println(StaticTest.string);
//如果不加static关键字 需要这样访问
StaticTest staticTest=new StaticTest();
System.out.println(staticTest.string);
//如果加上static关键字,上面的两种方法都可以使用
}
}
静态方法
被static修饰的方法是静态方法,静态方法不依赖于对象,不需要将类实例化便可以调用,由于不实例化也可以调用,所以不能有this,也不能访问非静态成员变量和非静态方法。但是非静态成员变量和非静态方法可以访问静态方法。
final
final关键字可以修饰类、方法、属性和变量
1.final修饰类,则该类不允许被继承,为最终类
2.final修饰方法,则该方法不允许被覆盖(重写)
3.final修饰属性:则该类的属性不会进行隐式的初始化(类的初始化属性必须有值)或在构造方法中赋值(但只能选其一)
4.final修饰变量,则该变量的值只能赋一次值,即常量。
如:
`public final static String SHI_YAN_LOU="shiyanlou";
权限修饰符
代码中经常用到pivate和public修饰符,权限修饰符可以用来修饰属性和方法的访问范围。
访问修饰符 | 本来 | 同包 | 子类 | 其他 |
---|---|---|---|---|
private | √ | |||
默认 | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
如图所示,代表了不同的访问修饰符的访问范围。比如private修饰的属性或者方法,只能在当前类中访问或者使用。
protected修饰的属性或者方法,对同一包内的类和所有子类可见。
封装
封装,即隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别。
封装的好处:
1.只能通过规定的方法访问数据
2.隐藏类的实例细节,方便修改和实现
如何实现封装?
1.修改属性的可见性,在属性的前面添加修饰符(private)
2.对每个值属性提供对外的公共方法访问,如创建 getter/setter(取值和赋值)方法,用于对私有属性的访问。
3.在 getter/setter 方法里加入属性的控制语句,例如我们可以加一个判断语句,对于非法输入给予否定。
首先在类里要将属性前添加private修饰符。然后定义getter和setter方法。修改People.java和NewObject.java的内容如下。
public class People {
//属性(成员变量)有什么,前面添加了访问修饰符private
//变成了私有属性,必须通过方法调用
private double height;
//属性已经封装好了,如果用户需要调用属性
//必须用getter和setter方法进行调用
//getter和setter方法需要程序员自己定义
public double getHeight(){
//getter 方法命名是get关键字加属性名(属性名首字母大写)
//getter 方法一般是为了得到属性值
return height;
}
//同理设置我们的setter方法
//setter 方法命名是set关键字加属性名(首字母大写)
//setter 方法一般是给属性值赋值,所以有一个参数
public void setHeight(double newHeight){
height = newHeight;
}
}
现在main函数里的对象不能再直接调用属性了,只能通过getter和setter方法进行调用。
public class NewObject {
public static void main(String[] args) {
People LiLei = new People();//创建了一个People对象LiLei
//利用setter方法为属性赋值
LiLei.setHeight(170.0);
//利用getter方法取属性值
System.out.println("LiLei的身高是"+LiLei.getHeight());
}
}
编译运行
javac NewObject.java People.java
java NewObject
Eclipse IDEA等IDE都有自动生成 getter 和 setter 方法的功能
实际操作:
1.单击右键,点击generate
2.点击getter或setter
3.选择一个属性
4.成功辣
this
this关键字代表当前对象。使用this.属性操作当前对象的属性,this.方法调用当前对象的方法。
如下:
public void setAge(int age){
this.age = age;
}
public int getAge(){
return age;
}
创建好了getter和setter方法后,我们发现方法中参数名和属性名一样。
当成员变量和局部变量之间发生冲突时,在属性名前面添加了this关键字。此时就代表将一个参数的值赋给当前对象的属性。同理this关键字可以调用当前对象的方法。
继承
继承可以看成是类与类之间的衍生关系。
继承需要符合的关系是:is-a,父类更通用,子类更具体。
1.语法:
class 子类 extends 父类
2.实例:
先创建一个父类Jojo.java
public class Jojo{
public String name;
public void teleport(){
System.out.println("乔家人一生一次的瞬移");
}
public String stand;
public int punchnum;
public String way_of_attack;
public void attack(int punchnum,String way_of_attack){
int i;
for(i=1;i<punchnum;i++){
System.out.print(way_of_attack);
}
System.out.println("!");
}
public void selfIntroduction(String name,String stand){
System.out.println("我叫"+name+"替身名是"+stand);
}
}
接下来创建一个子类GiornoGiovanna.java
public class GiornoGiovanna extends Jojo{
public void singMagic(){
System.out.println("我乔鲁诺·乔巴拿有一个梦想!那就是成为秧歌Star!");
}
}
再创建一个Main类测试一下
import java.util.Scanner;
public class Main{
public static void main(String[] args){
GiornoGiovanna giogio=new GiornoGiovanna();
giogio.selfIntroduction("乔鲁诺·乔巴拿","Gold Experience");
giogio.singMagic();
giogio.attack(44,"木大");
}
}
编译运行结果:
其实我这个例子有一点不好,那就是按理说不管子类还是父类,他都是类,应该是对象的抽象,而GiornoGiovanna这个类其实具体到一个对象了。没有抽象性。
继承的特点:
- 子类拥有父类除private以外的所有属性和方法
- 子类可以拥有自己的属性和方法
- 子类可以重写实现父类的方法
- Java中的继承是单继承,一个类只有一个父类
注:Java实现多继承的一个办法是implements(实现)接口
先mark一下,未来看implements
super
super关键字在子类内部使用,代表父类对象。
1.访问父类的属性super.属性名
2.访问父类的方法super.attack()
3.子类构造方法需要调用父类的构造方法时,在子类的构造方法体里最前面的位置:super()
方法重载与重写
方法重载
方法重载是指在一个类中定义多个同名的方法,但要求每个方法具有不同的参数的类型或参数的个数。方法重载一般用于创建一组任务相似但是参数不同的方法。
public class Test{
void f(int i){
System.out.println("i="+1);
}
void f(float f){
System.out.println("f="+f);
}
void f(String s){
System.out.println("s="+s);
}
void f(String s1,String s2){
System.out.println("s1+s2="+(s1+s2));
}
void f(String s,int i){
System.out.println("s="+s+",i="+i);
}
public static void main(String[] args){
Test test = new Test();
test.f(3456);
test.f(34.56f);
test.f("abc");
test.f("abc","def");
test.f("abc",3456);
}
}
方法重载有以下几种规则:
- 方法中的参数列表必须不同。比如:参数个数不同或者参数类型不同
- 重载的方法中允许抛出不同的异常
- 可以有不同的返回值类型,但是参数列表必须不同
- 可以有不同的访问修饰符
方法重写
子类可以继承父类的方法,但如果子类对父类的方法不满意,想在里面加入适合自己的一些操作时,就需要将方法进行重写,并且子类在调用方法中,优先调用子类的方法。
比如Jojo类中有attack这个方法代表了攻击,但是不同的JoJo有不同的叫法,比如乔太郎是欧拉欧拉,乔鲁诺是木大木大。
举例:
public class Jojo{
public void attack(){
System.out.println("喊招!");
}
}
public class GiornoGiovanna extends Jojo{
public void attack(){
System.out.println("木大 木大 木大 木大!!!");
}
}
写个Main类来看看输出结果:
public class Main{
public static void main(String args[]){
Jojo x = new Jojo();
GiornoGiovanna y = new GiornoGiovanna();
x.attack();
y.attack();
}
}
***
***
##多态
* 多态
多态是指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。多态也成动态绑定(dynamic binding),是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。
通俗的讲,只通过父类就能够引用不同的子类,这就是多态,我们只有在运行的时候才会知道引用变量所指向的具体实例对象。
* 向上转型
要理解多态必须明白什么是“向上转型”,比如,一段代码如下,GiornoGiovanna类是Jojo类的子类:
```java
Jojo x = new Jojo();//x是父类的引用指向的是本类的对象
Jojo y = new GiornoGiovanna();//y是父类的引用指向的是子类的对象
```
>注:不能使用一个子类的引用去指向父类的对象。
如果定义了一个指向子类对象的父类引用类型,那么它除了能够引用父类中定义的所有属性和方法外,还可以使用子类强大的功能。但是对于只存在于子类的方法和属性就不能获取。
例:(真的不想用GiornoGiovanna了,,改成GioGio了。)
class Jojo{
public void attack(){
System.out.println("欧拉!");
}
}
class GioGio extends Jojo{
/*子类和父类中都存在的方法*/
public void attack(){
System.out.println("木大!");
}
/*只存在于子类的方法*/
public void wry(){
System.out.println("wrrrrrrrry!!!");
}
public class Main{
public static void main(String[] args){
Jojo 空调 = new Jojo();/*指向父类对象的父类引用类型*/
Jojo 绒绒 = new GioGio();/*指向子类对象的父类引用类型*/
GioGio 茸茸 = new GioGio();/*指向子类对象的子类引用类型*/
空调.attack();/*输出"欧拉!"*/
绒绒.attack();/*输出"木大!"*/
//绒绒.wry();
//编译不通过
茸茸.atttack();/*输出"木大"*/
茸茸.wry();/*输出"wrrrrrrrry!!!*/
}
}
在这里,由于绒绒是父类的引用,指向子类的对象,因此不能获取子类的方法(wry()方法),同时当调用attack()方法时,由于子类重写了父类的attack()方法,所以调用子类中的attack()方法。
因此,向上转型,在运行时,会遗忘子类对象中与父类对象中不同的方法,也会覆盖与父类中相同(方法名、参数都相同)的方法————重写。
- 多态的实现条件
Java实现多态有三个必要条件:继承、重写和向上转型(即父类引用指向子类对象)。
只有满足上述三个条件,才能够在同一个继承结构中使用统一的逻辑实现代码处理不同的对象,从而达到执行不同的行为。 - 多态的实现方式
Java中多态的实现方式:继承父类进行方法重写,抽象类和抽象方法,接口实现。
抽象类
在定义类时,前面加上abstract关键字修饰的类叫抽象类。抽象类中有抽象方法,这种方法是不完整的,仅有声明而没有方法体。抽象方法声明语法如下:
abstract void f(); //f()方法是抽象方法
什么时候会用到抽象类呢?
1.在某些情况下,某个父类只是知道其子类应该包含怎样的方法,但无法准确知道这些子类如何实现这些方法。也就是说抽象类是约束子类必须要实现哪些方法,而并不关注方法如何去实现。
2.从多个具有相同特征的类中抽象出一个抽象类,以这个抽象类作为子类的模板,从而避免了子类设计的随意性。
所以可知,抽象类是限制规定子类必须实现某些方法,但不关注实现细节。
抽象类的实现规则如下:
1.用abstract修饰符定义抽象类
2.用abstract修饰符定义抽象方法,只用声明,不需要实现
3.包含抽象方法的类就是抽象类
4.抽象类中可以包含普通的方法,也可以没有抽象方法
5.抽象类的对象不能直接创建,通常是定义引用变量指向子类对象
举例(换个例子吧,一整篇JOJO有点疲劳..):
/*Yang.java*/
public abtract class Yang{
/*抽象类和抽象方法*/
public abstract eat();
public abstract hold();
}
/*LanYangYang.java*/
public class LanYangYang extends Yang{
public void eat(){
System.out.println("我喜欢吃青草蛋糕~!");
}
public void eat(){
System.out.println("我有一条黄色餐巾~!");
}
public static void main(String[] args){
LanYangYang smalllyy = new LanYangYang();
smalllyy.eat();
smalllyy.message();
}
}
接口
接口用于描述类所具有的功能,而不提供功能的实现,功能的实现需要在实现接口的类中,并且该类必须实现接口中所有的未实现方法。
接口的声明语法格式如下:
修饰符 interface 接口名称 [extends 其他的接口名]{
//声明变量
//抽象方法
}
如声明一个Yang接口:
/*Yang.java*/
interface Yang{
//int x;
//编译错误,x需要初始化,因为是static final类型
int y = 5;
public eat();
public travel();
}
注意点:在Java8中
- 接口不能用于实例化对象
- 接口中方法只能是抽象方法、default方法、静态方法
- 接口成员是static final类型
- 接口支持多继承
在Java9(但是我的是Java12呀..)中,接口可以拥有私有方法和私有静态方法,但是只能被该接口中的default方法和静态方法使用。
多继承实现方式:
修饰符 interface A extends 接口1,接口2{
}
修饰符 class A implements 接口1,接口2{
}
内部类
将一个类的定义放在另一个类的定义内部,这就是内部类。而包含内部类的类被称为外部类。
内部类的主要作用如下:
1.内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类。
2.内部类的方法可以直接访问外部类的所有数据,包括私有的数据。
3.内部类所实现的功能使用外部类同样可以实现,只是有时使用内部类更方便。
4.内部类允许继承多个非接口类型(具体将在以后的内容进行讲解)
注:内部类是一个编译时的概念,一旦编译成功,就会成为完全不同的两类。对于一个名为outer的外部类和其内部定义的名为inner的内部类,编译完成后出现outer.class和outer$inner.class两类。所以内部类的成员变量/方法名可以和外部类的相同。
成员内部类
举例:
/*Game.java*/
//外部类Game
public class Game{
private String name = "twilight princess";//外部类的私有属性
//内部类Zelda
public class Zelda{
String triForce = "PowerCourageWisdom";
//内部类的方法
public void zeldaInfo(){
System.out.println("访问外部类中的name:"+name);
System.out.println("访问内部类中的triForce:"+triForce);
}
}
//测试成员内部类
public static void main(String[] args){
Game ns = new Game();//创建外部类对象,对象名为ns
Zelda botw = ns.new Zelda();//使用外部类对象创建内部类对象,对象为botw
//或者为 Game.Zelda.botw = ns.new Game();
botw.zeldaInfo();//调用内部对象的zeldaInfo方法
}
}
编译的时候只要javac Game.java
即可。
成员内部类的使用方法:
1.Zelda类相当于Game的一个成员变量,所以Student类可以使用任意访问修饰符
2.Zelda类在Game类里,所以访问范围在类里的所有方法均可以访问Game的属性(即内部类里可以直接访问外部类的方法和属性,反之不行)
3.定义成员内部类后,必须使用外部类对象来创建内部对象,即内部类 对象名 = 外部类对象.new内部类();
4.如果外部类和内部类具有相同的成员变量或方法,内部类默认访问自己的成员变量或方法,如果要访问外部类的成员变量,可以使用 this 关键字。
注:成员内部类不能含有 static 的变量和方法,因为成员内部类需要先创建了外部类,才能创建它自己的。
静态内部类
静态内部类通常被称为嵌套类。
举例:
/*Beverage.java*/
//外部类Beverage
public class Beverage{
private String name = "dragon-well";//外部类的私有属性
/*外部类的静态变量。Java中被static修饰的成员称为静态成员或类成员。它属于整个类所有,而不是某个对象所有,即被类的所有对象所共享。静态成员可以使用类名直接访问,也可以使用对象名进行访问。*/
static String color="achromatic";
//静态内部类Tea
public static class Tea{
//内部类的成员属性
String color = "limpid brown";
//内部类的方法
public void teaInfo(){
System.out.println("访问外部类中的name:"+(new Beverage().name));
System.out.println("访问外部类中的color:"+Beverage.color);
System.out.println("访问内部类中的color:"+color);
}
}
//测试成员内部类
public static void main(String[] args){
Tea x = new Tea();//直接创建内部类对象,对象名为x
x.teaInfo();//调用内部对象的teaInfo方法
}
}
静态内部类是 static 修饰的内部类,这种内部类的特点是:
1.静态内部类不能直接访问外部类的非静态成员,但可以通过new 外部类().成员的方式访问。
2.如果外部类的静态成员与内部类的成员名称相同,可通过类名.静态成员访问外部类的静态成员;如果外部类的静态成员与内部类的成员名称不相同,则可通过成员名直接调用外部类的静态成员。
3.创建静态内部类的对象时,不需要外部类的对象,可以直接创建内部类 对象名 = new 内部类();
局部内部类
局部内部类,是指内部类定义在方法和作用域内。
// Adobe.java
//外部类Adobe
public class Adobe {
//定义在外部类中的方法内:
public void adobeInfo() {
final String price = "much"; //外部类方法中的常量
class ps {
String name = "photoshop"; //内部类中的常量
public void print() {
System.out.println("访问外部类的方法中的常量price:" + peice);
System.out.println("访问内部类中的变量name:" + name);
}
}
ps a = new ps(); //创建方法内部类的对象
a.print();//调用内部类的方法
}
//定义在外部类中的作用域内
public void adobeInfo2(boolean b) {
if(b){
final String price = "much"; //外部类方法中的常量
class ps{
String name = "photoshop"; //内部类中的常量
public void print() {
System.out.println("访问外部类的方法中的常量price:" + price);
System.out.println("访问内部类中的变量name:" + name);
}
}
ps a = new ps(); //创建方法内部类的对象
a.print();//调用内部类的方法
}
}
//测试方法内部类
public static void main(String[] args) {
People b = new People(); //创建外部类的对象
System.out.println("定义在方法内:===========");
b.peopleInfo(); //调用外部类的方法
System.out.println("定义在作用域内:===========");
b.peopleInfo2(true);
}
}
编译运行结果:
定义在方法内:===========
访问外部类的方法中的常量price:much
访问内部类中的变量name:photoshop
定义在作用域内:===========
访问外部类的方法中的常量price:much
访问内部类中的变量name:photoshop
局部内部类也像别的类一样进行编译,但只是作用域不同而已,只在该方法或条件的作用域内才能使用,退出这些作用域后无法引用。
匿名内部类
匿名内部类,顾名思义,就是没有名字的内部类。正因为没有名字,所以匿名内部类只能使用一次,它通常用来简化代码编写。但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口。
例如:
public class Outer{
public Inner getInner(final String name, String city){
return new Inner(){
private String nameStr = name;
public String getName(){
return nameStr;
}
};
}
public static void main(String[] args){
Outer outer = new Outer();
Inner inner = outer.getInner("Inner","NewYork");
System.out.println(inner.getName());
}
}
interface Inner{
String getName();
}
编译运行结果:
Inner
匿名内部类不能加访问修饰符。要注意的是,new 匿名类,这个类是要先定义的,如果不先定义,编译时会报错该类找不到。
同时,在上面的例子中,当所在的方法的形参需要在内部类里面使用时,该形参必须为final。这里可以看到形参 name 已经定义为 final 了,而形参 city 没有被使用则不用定义为 final。
然而,因为匿名内部类没名字,是用默认的构造函数的,无参数的,如果需要该类有带参数的构造函数,示例如下:
public Inner getInner(final String name, String city){
return new Inner(name,city){
private String nameStr = name;
public String gerName(){
return nameStr;
}
}
}
注意这里的形参 city,由于它没有被匿名内部类直接使用,而是被抽象类 Inner 的构造函数所使用,所以不必定义为 final。
参考
Java编程基础(实验楼)
css页面定制(为了评论里的看板娘)
代码段高亮(我一直失败的原因是博客园那个BlueCurve主题有毒..虽然我很喜欢
感想
编写第一个类时,我仿佛回到了被《笨方法学Python3》支配的时候。。(可以用它写个纯文字的游戏来着,,)
感觉学了C语言的结构体后,再回来学面向对象好像更易理解了一点。
我再也不想写GiornoGiovanna这么长的类名了......太傻了
to be continued
总有一天,我会用高亮的代码段让这里焕然一新!这是我的宣言!