Java入门- 面向对象的编程语言
一、java介绍
java是一个面向对象的编程语言,底层语言是C++,JVM是用C++写好的虚拟电脑
JDK是开发工具箱,其中有java最核心的库
java语言特性
java语言中真正操作内存的是:JVM(java虚拟机)
java屏蔽了指针概念,程序员不能直接操作内存
-
优点:简单,不容易导致内存泄漏,因为Java有一种机制:自动垃圾回收机制(GC机制)JVM负责调度GC机制。程序员无需干涉
-
缺点:效率问题,没c,c++效率高
-
计算机最主要的部件
-
CPU:中央处理器,相当于人类的大脑
-
内存 :程序运行过程中临时数据存储空间,断电或关机之后内存中的数据消失
-
硬盘:持久化设备,硬盘上的数据不会因断电而丢失
-
主板:相当于人类的躯干,是一个载体,CPU、内存条、硬盘等主要部件都是放在主板上,主板上有很多线,将以上部件连接起来
-
-
…
java完全支持多线程并发,具有可移植性/跨平台,同样的java程序可以在windows上运行,也能在Linux上还能在MacOS运行
JDK(java软件开发工具包)包括JRE,JRE(java运行环境)包括JVM(java虚拟机)
java程序不能直接运行,需经过编译,生成“字节码”,这个“字节码”可以跨平台运行。java源代码删除后只留下字节码仍可以运行。
源代码扩展名:xxx.java 字节码扩展名为:xxx.class
java虚拟机负责把字节码翻译成二进制的代码
-
在硬盘上某个位置,新建.java文件
-
使用某个文本编辑器打开
-
在文件中编写符合java语法规则的源代码
-
保存
-
使用Javac(JDK安装后自带)对java文件进行编译
-
符合语法规则的源代码通过生成字节码
-
-----运行期(JRE在起作用)----
7.如果在linux上运行,需要将windows上生成的class文件拷贝过去,无需源代码,真正运行的是字节码
8.使用JDK自带的一个命令/工具:java执行字节码
9.接下来交给JVM,JVM会将字节码装载进去,然后对字节码进行解释(解释器将字节码翻译成100101…)
10.JVM将生成的二进制码交给OS操作系统,操作系统会执行二进制码和硬件进行交互
开发Java程序
写完helloword.java
需要使用到的命令:D:\program\Java\jdk-17\bin\javac.exe
环境变量包括系统变量和用户变量,系统变量范围更大
javac命令怎么用
javac java源文件的路径
在当前文件路径下直接打cmd 能进入DOS命令窗口,该窗口起点即为该目录,🎃快速定位技巧。
java 后跟类名无需后缀 决不能跟路径,例: java HelloWord
Javac进行编译,java执行
运行java程序步骤
-
先cd切换到xxx.class文件所在路径
-
然后java xxx
-
D:\code_road_java\java_development\code>java HelloWord敲回车后发生了什么
-
启动JVM
-
JVM启动后,JVM启动“类加载器classloader”,类加载器会去硬盘上搜索:HellowWord.class
-
类加载器找不到字节码文件,错误:找不到或无法加载主类;如果找到对应的字节码文件,类加载器会将该字节码装载到JVM,JVM启动“解释器”将字节码解释为“1011010…"这种二进制,之后操作系统执行二进制码与硬件交互(默认情况下classloader从当前路径下加载文件)
-
如何让类加载器去指定路径下加载字节码文件?可通过设置classpath,该环境变量不属于windows操作系统,该环境变量隶属于java
👺计算机->右键-》属性-》高级系统设置-》环境变量-》新建
高版本java 可以不用编译,直接使用java 后跟绝对路径.HelloWord.java
java中的注释不会出现在字节码中
// 属于单行注释
/* */属于多行注释
类里面的东西叫做类体,public static void main(String[] args){中跟方法体}
对于主方法而言,只有args可以修改。
一个java源文件可以定义多个class,编译结果可以生成多个class。public的类不是必须的。如何有public class ,public修饰 的类名必须和源文件名保持一致。public的类有也只能有一个。
二、标识符和关键字
在editplus工具中显示的高亮颜色为黑色时,这个单词属于标识符
标识符可以标识类名、方法名、变量名、接口名、常量名…
凡是程序员自己有权命名的单词都是标识符
-
关键字不能做标识符
-
标识符不能以数字开头
-
标识符只能由数字、字幕、下划线、美元符号组成
-
对于类名来说,不区分大小写,Hello和hello一样,谁在前面生成谁
-
具体的命名规范
-
见名知意
-
遵循驼峰命名方式,一高一低
-
类名和接口名首字母大写,后面每个单词首字母大写
-
变量名和方法名首字母小写。后面每个单词首字母大写
-
所有常量名全部大写,单词间采用下划线进行衔接
在EditPlus中以蓝色字体形式存在的都是关键字 关键字例:public、int、long、float…
注意字符型用单引号,字符串型用双引号 如若使用不当会出现编译错误
javadoc可以提取注释生成帮助文档
三、变量
数据类型的作用?
根据不同的数据类型,在内存中分配不同大小的空间
方法体内容自下而上的顺序依次逐行运行
同一个域中变量名不能重名
一行可以同时声明多个变量 int a,b,c = 100;注意a,b这里没有值,但是编译能通过
方法体中声明的变量:局部变量(只在方法体内有效,方法结束,内存空间释放)
方法体之外,类体内声明的变量:成员变量
java中有一重要原则:就近原则
for(int n=0;n<10;n++) 这里n只属于for域,出了大括号就不认识
四、数据类型
基本数据类型可以划分为4大类8小种:
-
整数型 byte,short,int,long (没有小数) 1字节,2字节,4字节,8字节
-
浮点型 float.,double (带有小数)4字节,8字节
-
布尔型 boolean 1字节
-
字符型 char 2字节
1字节=8bit
1比特就是一个1或0
1kb=1024byte,1MB=1024KB,1GB=1024MB,1TB=1024GB
byte b=2;在计算机中表示形式:00000010
short s=2;在计算机中表示形式:00000000 00000010
int i=2;;在计算机中表示形式:00000000 00000000 00000000 00000010
十进制213转化成二进制,方法:一直除以2,余数逆序输出
二进制转十进制:例
10111
,
2
0
∗
1
+
2
1
∗
1
+
2
2
∗
1
+
2
3
∗
0
+
2
4
∗
1
=
23
10111,2^0*1+2^1*1+2^2*1+2^3*0+2^4*1=23 10111,20∗1+21∗1+22∗1+23∗0+24∗1=23
byte:[-128~127]
short:[-32768,32767]
int:[-21474983648~21474983647]
char[0~65535]
字符编码是针对char类型计算机表示问题而诞生的
字符编码是人为定义的一套转换表
本质上就是一本字典,该字段描述了文字与二进制之间的对照关系。字符编码是人为规定的。
英文对应的字符编码方式是:ASCII码
ASCII码采用1byte进行储存,英文字母是26个,1byte可以表示256种不同的情况
‘a’—>97 采用ASCII进行编码 ->01100001 ->解码 后–>‘a’
‘A’—>65 …
‘0’ —>48 …
随着计算机语言的发展,后来国际标准组织制定了ISO-8859-1编码方式,又称为latin-1,向上兼容ASCII码,但不支持中文
中文编码方式:GB2312<GBK<GB18030
繁体中文 :big5
java 为了支持全球所有文字,采用unicode编码,具体实现包括:UTF-8 UTF-16 UTF-32
char 只能储存一个字
转义字符
\t 表示制表符tab
System.out.println()与System.out.print()区别,一个换行一个不换行
\' 表示一个普通的'单引号 \\表示一个普通的\字符
char x = ‘\u4e2d’ \u后面是一个字符的unicode编码,unicode编码是十六进制。
类型转换
在任何情况下,整数型的“字面量/数据”默认被当做int类型处理
long e = 2147483648; 编译会报错,因为整数会被默认为int,该数字超出int的范围,这段代码会将int转化为long,在没转换之前已经超出范围,解决办法: long e=2147483648L;
long x =100L;
int y=x;
编译会报错,大容量不能直接赋值给小容量。小容量可以直接赋值给大容量。
long x =100L;
**int y =(int)x;**可进行强制转换,运行时可能损失精度(long强行转int会将前面的4个字节砍掉)
例如long类型,八个字节 共64个byte 0000000 00000000 ...00000000 00000010
int类型,4个字节,共32个byte
byte z=127;可以通过编译,整数型字面量可以直接赋值给byte类型的变量,只要不超出byte的范围
short z=32767;这个同上
当一个整数赋值给char类型变量的时候,会自动转换成char字符型,最终结果是一个字符
long a-10L;
char c='a';
short s=100;
int i=30;
int x= a+c+s+i ;会编译报错,因后面的累加起来是long类型,正确的方法加个强制类型转换
int (int)x= a+c+s+i ;
char+short+byte 这个除外,多种数据类型做混合运算的时候,最终结果类型是“最大容量”对应的类型。
任何一个浮点型数据默认被当做double来处理,若想让这个浮点型字面量当做float来处理,需在字面量后面添加F或者f
int i = 10.0/5;会将5先转化为double然后进行运算
笔试题的所有范围,在以上六条规则中。
五、运算符
++ 变量自加1
– 变量自减1
i++;
i++;皆可
int m =10;
int n = m++;运行结束后, 这里n为10,m为11
System.out.println(c++); 可以转换 int temp = c++; System.out.println(temp);
逻辑运算符
& 逻辑与 并且 | 逻辑或 或者
! 逻辑非 取反 && 短路与 int x=10; int y=11; System.out.println(x>y && x>y++); 这里用短路与,若左边为false,右边不执行,故y=11. 若用&逻辑与,右边会继续执行,y=12。故大部分情况,使用短路与&& || 短路或
赋值运算符
使用扩展赋值运算符,永远不会改变运算结果类型
+= | -= | *= | /= | %= |
byte x=100;
x =x +1;编译会出错
x += 1;编译成功 等同于x = (byte)(x+1)
条件运算符
语法格式:布尔表达式 ?表达式1:表达式2
当布尔表达式为True,表达式1的执行结果作为真个表达式的结果
boolean sex=False;
char c = sex ? ‘男’ :‘女’; 返回结果 ‘女’
System.out.println(这里面什么类型的数据都能放)
字符串连接运算符
作用1:求和
作用2:字符串拼接
六、控制语句
1.怎么接受用户键盘输入?
java.util.Scanner s = new java.util.Scanner(System.in);
int i = s.nextInt();
String str=s.next();
2.控制语句
三类
-
选择语句 if switch
-
循环语句 for while do…while
-
转向语句 break continue return
boolean sex =false; if (sex){ System.out.println("男"); }else{ System.out.println("女"); } System.out.println(sex ? "男":"女");
switch语句,
支持int类型以及string类型
switch(值){ case 值1: java语句 break case 值2: java语句 break case 值3: java语句 break default: java语句 } 拿值与值1进行比较,如果相同,则执行该分支的java语句 case穿透现象,若没有break,则直接执行下一个case里面的java语句,不会在进行比较。
例
java.util.Scanner s = new java.util.Scanner(System.in); int num =s.nextInt(); switch(num){ case 0: System.out.println('星期日') break case 1: System.out.println('星期一') break case 2: System.out.println('星期二') break case 3: System.out.println('星期三') break case 4: System.out.println('星期四') break case 5: System.out.println('星期五') break case 6: System.out.println('星期六') }
循环语句
for(int i=0;i<100;i++){
System.out.println()
}
for(初始化表达式;条件表达式;更新表达式){循环体}
do while
语法机制
do{ 循环体; }while(布尔表达式);
先执行循环体的代码,之后判断布尔表达式结果,如果为true,则继续执行,到false结束。
转向语句
break用在两个地方,一是switch,用来终止switch语句的执行;二是用在循环语句中,用来终止循环的执行。
七、方法
main方法jvm会自动调用,除此之外的其他方法都需要程序员手动调用
java的方法相当于c语言或python的函数。
1.方法怎么定义,语法机制是什么?
[修饰符列表] 返回值类型 方法名(形式参数列表){
方法体
}
当一个方法执行结束不返回任何值的时候,返回值类型写void关键字
有返回值类型时,方法里必须有return并且返回相应类型的结果
形式参数列表中的每个参数都是“局部变量”,方法结束后内存释放,形参的个数是:0~N个。
public static void sumInt(int x,int y){}
return用来终止离它最近的一个方法
JVM(java虚拟机)
-
栈 stack:在方法被调用的时候,该方法需要的内存控在栈中分配
-
堆区
-
方法区:类加载器classloader,将硬盘上的xxx.class字节码文件装载到jvm的时候,会将字节码文件存放到方法区当中,也就是说方法区中储存的是代码片段
常见的数据结构
数组、链表、图、二叉树、栈、队列…
和数据结构通常一块出现的是算法
排序算法、查找算法…
方法只有在调用的时候才会在栈中分配空间,并且调用时是压栈。执行结束后,该方法所需的空间就会释放,此时发生弹栈动作。
方法重载机制
功能相似的,可以让方法名相同,更易于代码编写。
在同一个类当中,如果“功能1”和“功能2”它们的功能是相似的,那么可以考虑将它们的方法名一致。
-
在同一个类中
-
方法名相同
-
参数列表不同(参数个数不同,类型不同,顺序不同都算不同)
方法递归
方法自己调用自己,这就是方法递归。
如果遇到stackoverflowerror
-
先检查递归的结束条件是否正确
-
手动调整JVM的栈内存初始化大小。可以将栈内存空间调大些。
-
调整了大小,如果运行还是出现这个错误,继续调整栈的内存大小 java -X可以查看
public clss RecursionTest03{ public static void main(String[] args){ int dd = sum(3); S.p(dd); }
public static int sum(int n){ if(n==1){ return 1; } return n + sum(n-1); } }
八、认识面向对象
-
c语言是完全面向过程
-
c++一般面向过程,一半面向对象
-
java完全面向对象
-
面向过程的优点(快速开发)
-
对于小型项目,采用面向过程的方式,效率高。不需要前期进行对象的提取,模型的建立。
面向对象的三个术语
-
OOA:面向对象分析
-
OOD:面向对象设计
-
OOP:面向对象编程
实现一个软件的过程:
分析(A)–>设计(D)–>编程(P)
PM项目经理 Project Manager
面向对象包括三大特征
-
封装
-
继承
-
多态
类:人类大脑思考总结的一个模板
对象:实际存在的个体
实例:对象的另一个名称
实例化:通过类这个模板创建对象的过程
抽象:多个对象具有共同特征,进行思考总结抽取共同特征的过程
九、对象的创建与使用
类的定义
[修饰符列表] class 类名{ } Student s1 = new Student();
类名 变量名= new 类名();
凡是通过new运算符创建的对象,都存储在堆内存中,new运算符的作用就是在堆内存中开辟一块空间
栈中主要存储局部变量
变量必须先声明,再赋值才能访问
对于成员变量来说, 没有手动赋值时,系统默认赋值
不能通过类名直接访问实例变量
像s1这种变量,保存了对象内存地址的变量,有一个特殊的名字:引用
构造方法
-
通过构造方法可以完成对象的创建,以及实例变量的初始化。换句话说,构造方法是用来创建对象,并且同时给对象的属性赋值
-
当一个类没有提供任何构造方法,系统会默认提供一个无参数的构造方法。该方法被称为缺省构造器。
-
调用构造方法,使用new运算符 new 构造方法名(实际参数列表)
-
语法结构?
-
[修饰符列表] 构造方法名(形式参数列表){ 构造方法体 }
-
修饰符列表目前统一写public,不要写public static
-
构造方法名和类名必须一致
-
构造方法不需要制定返回值类型,也不能写void,写上void表示普通方法,就不是构造方法了。
[修饰符列表] 返回值类型 方法名(形式参数列表){ 方法体 }
成员变量,当构造方法里什么都没写,会默认成员变量会默认值。
构造方法的作用
-
完成对象的创建
-
给属性(实例变量)初始化
-
十、封装
作用两个
-
保证内部结构安全
-
屏蔽复杂,暴露简单
-
封装流程
-
属性私有化,使用private关键字进行修饰
-
对外提供简单的操作入口
封装前 public class PersonTest{ public static void main(String[] args){ Person p1 = new Person(); S.p(p1.age); p1.age=-100; S.p(p1.age); }
public class Person{ int age } } 封装后 public class PersonTest{ public static void main(String[] args){ Person p1 = new Person(); S.p(p1.age); p1.age=-100; S.p(p1.age); }
public class Person{ private int age
} }
带有static的方法,通过“类名.”方式访问
将构造方法中class换成void变成实例方法
对象又被称为实例,有实例方法、实例变量。实例对象必须创建对象后,才能通过“引用.”的方法访问实例方法
空引用访问“实例相关的数据”,会出现空指针异常
set方法和get方法
public calss Person{ private int age; public int getAge(){ return age; } public void setAge(int nianLing){ age = nianLing; } }
public calss Person{ private int age; public int getAge(){ return age; }
public void setAge(int nianLing){ if(nianLing<0 || nianLing>150){ S.p("年龄不合法,请重新赋值"); return; } age = nianLing; } }
外部读取
public class PersonTest02{ public static void main(String[] args){ Person p1 = new Person(); int nianLing = getAge(); S.p(nianLing);
S.p(p1.getAge()); p1.setAge(100); S.p(p1.getAge()); } }
十一、this和static
-
static翻译为“静态”
-
所有static关键字修饰的都是类相关的,类级别的
-
所有static修饰的,都是采用“类名.”方式访问
-
static修饰的变量:静态变量
-
static修饰的 方法:静态方法
-
根据变量声明的位置可以划分为局部变量和成员变量,而成员变量又可以分成实例变量和静态变量。实例变量采用“引用”的方法访问
静态变量在类加载时初始化,不需要new对象,静态变量的空间就开出来了。静态变量存储在方法区
静态变量也可以用“引用”的方法访问,不过建议不这么写,会引起误解,使程序员认为是实例的变量。
静态代码块
static{
}
static在main方法之前执行,自上而下执行。
该代码不常用,作用:给java程序员的一个特殊时机,这个时机叫做:类加载时机。
记录一下类加载的日志信息(在哪年哪月哪日…哪个类加载到JVM当中)
实例语句块
语法就一个大括号{}
在构造方法之前执行 ,作用当有多个构造方法时,且构造方法中开头有相同的代码,可以提取相同代码放入实例语句块
this
this只能用在实例方法中,谁调用这个实例方法,this就是谁。this代表的是:当前对象。
作用:当方法中要用到对象实例变量,而对象还没创建出来时,怎么写,this.实例变量,可以省略。(实例变量必须采用“引用”的方式访问)
this不能出现在静态方法中,this表示当前对象,静态方法中不存在当前对象
不能省略this的情况
public void setName(String s){ name =s; } public void setName(String name){ this.name = name; }
this()的用法
public class thistest(){ public static void main(String[] args){ } } class Date{ private int year; private int month; private int day; public Date(){ this.year = 1970; this.month = 1; this.day=1; } public Date(int year,int month,int day){ this.year =year; this.month = month; this.day=day; } public void setYear(int year){ this.year = year; } public int getYear(){ return year; } public void setMonth(int month){ this.month = month; } public int getMonth(){ return month; } public void setDay(int day){ this.day = day; } public int getDay(){ return day; } public void detail(){ S.p("打印输出年月日") } }
-
this是一个关键字
-
this可以使用在实例方法中,也可以使用在构造方法中
-
this出现在实例方法中表示当前对象
-
this不能出现在静态方法中
-
大部分时间可以省略,但是用来区分局部变量和实例变量的时候不能省略
-
this() 这种语法只能出现在构造方法第一行,表示当前构造方法调用本类其他的构造方法,目的是代码复用
-
十二、继承
子类继承父类,除了构造方法全部继承过来。
凡是采用“is a”能描述的,都可以继承。例如:
猫是一个动物
狗是一个动物
信用卡账户是一个银行账户…
JDK源码所在位置java/jdk-17/lib/src.zip
System.out.println(); 这里system是一个类,out属于静态变量
十三、方法覆盖和多态 ***
1.什么时候考虑使用方法覆盖?
2.什么条件满足的时候构成方法覆盖?
3.关于object类中toString()方法覆盖?(toString的作用…大多数java类toString方法都需要覆盖,因为object类中提供的toString方法输出的是一个java对象的内存地址)
4.方法重载与方法覆盖有什么区别?(方法重载发生在同一个类中,方法覆盖发生在具有继承关系的父子类之间。条件不同…)
方法重载 overload
当在一个类当中,如果功能相似的话,建议将名字定义成一样,条件如下
-
在同一个类当中
-
方法名相同
-
参数列表不同
-
方法覆盖
当子类继承父类之后,当继承过来的方法无法满足当前之类的业务需求时,子类有权利对这个方法进行重新编写,有必要进行“方法的覆盖”
当子类对父类继承过来的方法进行“方法覆盖”后,继承过来的方法没了。
实行方法覆盖的条件
-
两个类必须有继承关系
-
重写之后的方法与之前的 方法必须满足方法名相同,参数列表相同,返回值类型相同
-
访问权限不能更低,可以更高
-
重写之后的方法不能比之前方法抛出更多的异常,可以更少
注意事项
-
方法覆盖只针对方法和属性无关
-
私有方法无法覆盖
-
构造方法不能被继承,所有构造方法也不能被覆盖
-
方法覆盖只是针对于实例方法,静态方法覆盖没有意义
toString 含义:调用一个java对象的toString()方法就可以将该java对象转换成字符串的表示形式
多态
❓ 问题
-
向上转型和向下转型的概念?
-
什么是多态?
-
什么时候必须用向下转型?(当你需要访问的是子类对象中“特有”的方法,此时必须使用)
-
为什么使用多态,而不是在创建对象时直接使用子类,多态还需向上转型,之后继续向下转型,多此一举。(在公司写代码时,你只参加部分方法的编写,而该方法编写时,你不清楚其他人要传入其中是什么,只知道是Animal这类下的子类,而具体是Bird类还是Cat类还是其他的不清楚,故此时需要用到向下转换)
在进行多态学习时需先掌握两个概念
-
向上转型 子 ---->父 类比(自动类型转换)
-
向下转型 父----> 子 (强制类型转换)
无论是向上还是向下转型,两类之间必须有继承关系。
public class test01 { public static void main(String[] args){ Animal a1 = new Animal(); a1.move(); cat c1 = new cat(); c1.move(); Bird b1 = new Bird(); b1.move(); Animal a2 = new cat(); a2.move(); Animal a5 = new cat(); a5.catchMouse(); cat x = (cat)a5; x.catchMouse(); } }
多态指的是:父类型引用指向子类型对象,包括编译阶段和运行阶段。编译阶段:静态绑定父类 的方法。运行阶段:动态绑定子类型对象的方法 。多种形态。
Animal a6 = new Bird();
cat y = (cat)a6;
y.catchMouse();
会出现一个异常名为:java.lang.ClassCastException类型转换异常 ***经典异常 与空指针异常一样非常重要
instanceof(运行阶段动态判断)
-
instanceof可以在运行阶段动态判断引用指向对象的类型
-
instanceof的语法:(引用 instanceof 类型)
-
instanceof运算符的结果只有true或false
-
c是一个引用,c变量保存了内存地址指向了堆中的对想,(c instanceof Cat)返回True表示c引用指向的堆内存中的java对象是一个cat。
if(a6 instanceof cat){ cat y = (cat)a6; y.catchMouse(); }
当要用到向下转型时,一定要使用instanceof运算符进行判断,可以有效避免类型转换异常
多态在开发中的作用 *********
作用:降低程序的耦合度,提高程序的扩展力。
public class test { public static void main(String[] args){ Master zhangsan = new Master(); Dog caiQuan = new Dog(); zhangsan.feed(caiQuan); Cat lanMao = new Cat(); zhangsan.feed(lanMao); } } public class Master { public void feed(Cat c){ c.eat(); } public void feed(Dog d){ d.eat(); } } public class Pet { public void eat(){} } public class Master { public void feed(Pet p){ p.eat(); } }
私有方法不能覆盖
//方法重构时,返回值类型变小是可以的,变大不行。
十四、super
super和this对比着学习
this:
-
this能出现在实例方法和构造方法中
-
this的语法是:this. 和this()
-
this不能出现在静态方法中
-
this大部分情况可省略
-
this在区分局部变量和实例变量时不能省略
-
this()只能出现在构造方法第一行
-
super:
-
super能出现在实例方法和构造方法中
-
super的语法是:super. 和super()
-
super不能出现在静态方法中
-
super大部分情况可省略
-
super …可以省略
-
super()只能出现在构造方法第一行,通过当前的构造方法去调用“父类”中的构造方法,目的是:创建子类对象的时候,先初始化父类型特征
super()表示通过子类的构造方法调用父类的构造方法。模拟现实世界中:要有儿子,需要现有父亲
super();会默认出现在构造方法第一行,就像是没有构造方法时,会默认一个无参数构造方法。
super(实参)的作用是:初始化当前对象的父类型特征
super什么时候不能被省略?java允许父类和子类出现同名的变量,如果想在子中访问父的特征,super不能被省略
只写this和this.toString是一样的,但是super是不能单独用,必须在使用时加点