原文连接:https://foochane.cn/article/2...html
字节是计算机中最小存储单元。计算机存储任何的数据,都是以字节的形式存储。8个bit(二进制位) 0000-0000表示为1个字节,写成1 byte或者1 B。java
虚拟机是一种抽象化的计算机,经过在实际的计算机上仿真模拟各类计算机功能来实现的。Java虚拟机(Java Virtual Machine,JVM )有本身完善的硬体架构,如处理器、堆栈、寄存器等,还具备相应的指令系统。Java虚拟机屏蔽了与具体操做系统平台相关的信息,使得Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就能够在多种平台上不加修改地运行。编程
所谓的java虚拟机,就是一台虚拟的机器。它是一款软件,用来执行一系列虚拟计算机指令,大致上虚拟机能够分为系统虚拟机和程序虚拟机。Visual Box、VMWare就属于系统虚拟机。他们彻底是对物理计算机的仿真,提供一个可运行完整操做系统的软件平台。而java虚拟机就是典型程序虚拟机,它专门为执行单个计算机程序而设计,在java虚拟机中执行的指令咱们称之为java字节码指令。java发展到今天,出现了不少虚拟机,最初sun使用的叫Classic的java虚拟机,到如今使用最普遍的是HotSpot虚拟机,除了sun之外还有BEA的JRockit,目前JRockit和HotSpot都被甲骨文公司收入旗下,大有整合的趋势。segmentfault
任何软件的运行,都必需要运行在操做系统之上,而咱们用Java编写的软件能够运行在任何的操做系
统上,这个特性称为Java语言的跨平台特性。该特性是由JVM实现的,咱们编写的程序运行在JVM上,而JVM运行在操做系统上。数组
咱们想要运行一个已有的Java程序,那么只需安装 JRE 便可。咱们想要开发一个全新的Java程序,那么必须安装 JDK 。安全
常量分类:架构
类型 | 含义 | 举例 |
---|---|---|
整数常量 | 全部的整数 | 0,1, 567, -9 |
小数常量 | 全部的小数 | 0.0, -0.1, 2.55 |
字符常量 | 单引号引发来,只能写一个字符,必须有内容 | 'a' , ' ', '好' |
字符串常量 | 双引号引发来,能够写多个字符,也能够不写 | "A" ,"Hello" ,"你好" ,"" |
布尔常量 | 只有两个值 | true , false |
空常量 | 只有一个值 | null |
变量分类:编程语言
Java的数据类型分为两大类:ide
数据类型 | 关键字 | 占用内存 | 取值范围 |
---|---|---|---|
字节型 | byte | 1个字节 | -128~127 |
短整型 | short | 2个字节 | -32768~32767 |
整型 | int(默认) | 4个字节 | $-2^{31}$~$2^{31}-1$ |
长整型 | long | 8个字节 | $-2^{63}$~$2^{63}-1$ |
单精度浮点数 | float | 4个字节 | 1.4013E-45~3.4028E+38 |
双精度浮点数 | double(默认) | 8个字节 | 4.9E-324~1.7977E+308 |
字符型 | char | 2个字节 | 0-65535 |
布尔类型 | boolean | 1个字节 | true,false |
long
类型:建议数据后加L
表示
float
类型:建议数据后加F
表示
自动类型转换(隐式)函数
强制类型转换(显式)
注意事项:
byte/short/char
这三种类型均可以发生数学运算,例如加法“+
”.byte/short/char
这三种类型在运算的时候,都会被首先提高成为int类型,而后再计算。boolean
类型不能发生数据类型转换符号 | 说明 |
---|---|
+ | 加法运算,字符串链接运算 |
- | 减法运算 |
* | 乘法运算 |
/ | 除法运算 |
% | 取模运算,两个数字相除取余数 |
++ 、 -- | 自增自减运算 |
前++和后++的区别
public static void main(String[] args) { int a = 1; int b = ++a; System.out.println(a);//计算结果是2 System.out.println(b);//计算结果是2 }
public static void main(String[] args) { int a = 1; int b = a++; System.out.println(a);//计算结果是2 System.out.println(b);//计算结果是1 }
符号 | 说明 |
---|---|
= | 等于号 |
+= | 加等于 |
- = | 减等于 |
*= | 乘等于 |
/= | 除等于 |
%= | 取模等 |
符号 | 说明 |
---|---|
== | 比较符号两边数据是否相等,相等结果是true。 |
< | 比较符号左边的数据是否小于右边的数据,若是小于结果是true。 |
> | 比较符号左边的数据是否大于右边的数据,若是大于结果是true。 |
<= | 比较符号左边的数据是否小于或者等于右边的数据,若是小于结果是true。 |
>= | 比较符号左边的数据是否大于或者等于右边的数据,若是小于结果是true。 |
! = | 不等于符号 ,若是符号两边的数据不相等,结果是true。 |
符号 | 说明 | ||
---|---|---|---|
&& 短路与 | 1. 两边都是true,结果是true<br/>2. 一边是false,结果是false<br/>短路特色:符号左边是false,右边再也不运算 | ||
\ | \ | 短路或 | 1. 两边都是false,结果是false<br/>2. 一边是true,结果是true<br/>短路特色: 符号左边是true,右边再也不运算 |
! 取反 | 1. ! true 结果是false<br/>2. ! false结果是true |
三元运算符格式:
数据类型 变量名 = 布尔类型表达式?结果1:结果2
示例:
public static void main(String[] args) { int i = (1==2 ? 100 : 200); System.out.println(i);//200 int j = (3<=4 ? 500 : 600); System.out.println(j);//500 }
JShell脚本工具是JDK9的新特性,当咱们编写的代码很是少的时候,而又不肯意编写类,main方法,也不肯意去编译和运行,这个时候可使用JShell工具。
启动JShell工具,在DOS命令行直接输入JShell命令。
快捷键 | 功能 |
---|---|
Alt + Enter | 导入包,自动代码修正 |
Ctrl+Y | 删除光标所在行 |
Ctrl+D | 复制光标所在行的内容,插入光标位置下面 |
Ctrl+Alt+L | 格式化代码 |
Ctrl+/ | 单行注释 |
Ctrl+Shift+/ | 选中代码注释,多行注释,再按取消注释 |
Alt+Ins | 自动生成代码,toString,get,set等方法 |
Alt+Shift+ 上下箭头 | 移动当前代码行 |
Shift+F6 | 同时修改不一样地方的同一个量 |
输入sout | System.out.println(); |
输入psvm | public static void main(String[] args) |
输入5.fori | for(int i = 0; i < 5; i++) |
输入arr.fori或者arr.forr | for循环变量数组 |
语句格式:
if (判断条件1) { 执行语句1; } else if (判断条件2) { 执行语句2; } ... }else if (判断条件n) { 执行语句n; } else { 执行语句n+1; }
语句格式:
switch(表达式) { case 常量值1: 语句体1; break; case 常量值2: 语句体2; break; ... default: 语句体n+1; break; }
语句格式:
for(初始化表达式①; 布尔表达式②; 步进表达式④){ 循环体③ }
执行流程
执行顺序:①②③④ >②③④>②③④…②不知足为止。
①负责完成循环变量初始化
②负责判断是否知足循环条件,不知足则跳出循环
③具体执行的语句
④循环后,循环条件所涉及变量的变化状况
语句格式1:
初始化表达式① while(布尔表达式②){ 循环体③ 步进表达式④ }
执行流程
执行顺序:①②③④ >②③④>②③④…②不知足为止。
①负责完成循环变量初始化。
②负责判断是否知足循环条件,不知足则跳出循环。
③具体执行的语句。
④循环后,循环变量的变化状况。
语句格式2:
初始化表达式① do{ 循环体③ 步进表达式④ }while(布尔表达式②);
执行流程
执行顺序:①③④ >②③④>②③④…②不知足为止。
①负责完成循环变量初始化。
②负责判断是否知足循环条件,不知足则跳出循环。
③具体执行的语句
④循环后,循环变量的变化状况
使用场景:终止 switch或者循环
示例:
public static void main(String[] args) { for (int i = 1; i<=10; i++) { //需求:打印完两次HelloWorld以后结束循环 if(i == 3){ break; } System.out.println("HelloWorld"+i); } }
使用场景:结束本次循环,继续下一次的循环
示例:
public static void main(String[] args) { for (int i = 1; i <= 10; i++) { //需求:不打印第三次HelloWorld if(i == 3){ continue; } System.out.println("HelloWorld"+i); } }
容器: 是将多个数据存储到一块儿,每一个数据称为该容器的元素。
数组概念: 数组就是存储数据长度固定的容器,保证多个数据的数据类型要一致。
格式:
数组存储的数据类型[] 数组名字 = new 数组存储的数据类型[长度];
示例:
int[] arr = new int[3];
格式:
数据类型[] 数组名 = new 数据类型[]{元素1,元素2,元素3...};
示例:
int[] arr = new int[]{1,2,3,4,5};
格式:
数据类型[] 数组名 = {元素1,元素2,元素3...};
示例:
int[] arr = {1,2,3,4,5};
【注意】:
方式三
一样也进行了new
操做数组名 .length
,属性length的执行结果是数组的长度,int类型结果。由次能够推断出,数组的最大索引值为 数组名 .length-1
。数组名[索引]
示例:
public static void main(String[] args) { //定义存储int类型数组,赋值元素1,2,3,4,5 int[] arr = {1,2,3,4,5}; //为0索引元素赋值为6 arr[0] = 6; //获取数组0索引上的元素 int i = arr[0]; System.out.println(i); //直接输出数组0索引元素 System.out.println(arr[0]); }
代码以下:
public static void main(String[] args) { int[] arr = { 5, 15, 2000, 10000, 100, 4000 }; //定义变量,保存数组中0索引的元素 int max = arr[0]; //遍历数组,取出每一个元素 for (int i = 0; i < arr.length; i++) { //遍历到的元素和变量max比较 //若是数组元素大于max if (arr[i] > max) { //max记录住大值 max = arr[i]; } } System.out.println("数组最大值是: " + max); }
代码以下:
public static void main(String[] args) { int[] arr = { 1, 2, 3, 4, 5 }; /* 循环中定义变量min=0最小索引 max=arr.length‐1最大索引 min++,max‐‐ */ for (int min = 0, max = arr.length ‐ 1; min <= max; min++, max‐‐) { //利用第三方变量完成数组中的元素交换 int temp = arr[min]; arr[min] = arr[max]; arr[max] = temp; } // 反转后,遍历数组 for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } }
内存是计算机中的重要原件,临时存储区域,做用是运行程序。咱们编写的程序是存放在硬盘中的,在硬盘中的程序是不会运行的,必须放进内存中才能运行,运行完毕后会清空内存。
Java虚拟机要运行程序,必需要对内存进行空间的分配和管理。
Java的内存须要划分红为5个部分:
栈(Stack) :存放的都是方法中的局部变量。方法的运行必定要在栈当中运行。
堆(Heap):凡是new出来的东西,都在堆当中。
堆内存里面的数据,都有默认值。规则:
0
0.0
'\u0000'
false
null
示例:
public static void main(String[] args) { // 定义数组,存储3个元素 int[] arr = new int[3]; //数组索引进行赋值 arr[0] = 5; arr[1] = 6; arr[2] = 7; //输出3个索引上的元素值 System.out.println(arr[0]); System.out.println(arr[1]); System.out.println(arr[2]); }
代码执行流程:
两个变量指向同一个数据:
public static void main(String[] args) { // 定义数组,存储3个元素 int[] arr = new int[3]; //数组索引进行赋值 arr[0] = 5; arr[1] = 6; arr[2] = 7; //输出3个索引上的元素值 System.out.println(arr[0]); System.out.println(arr[1]); System.out.println(arr[2]); //定义数组变量arr2,将arr的地址赋值给arr2 int[] arr2 = arr; arr2[1] = 9; System.out.println(arr[1]); }
上述代码中,arr和arr2都指向同一个内存地址,arr2[1] = 9
执行后,arr[1]
也会跟着改变。
Java语言是一种面向对象的程序设计语言,而面向对象思想是一种程序设计思想,咱们在面向对象思想的指引下,
使用Java语言去设计、开发计算机程序。 这里的对象泛指现实中一切事物,每种事物都具有本身的属性和行为。面向对象思想就是在计算机程序设计过程当中,参照现实中事物,将事物的属性特征、行为特征抽象出来,描述成计算机事件的设计思想。 它区别于面向过程思想,强调的是经过调用对象的行为来实现功能,而不是本身一步一步的去操做实现。
面向对象思想是一种更符合咱们思考习惯的思想,它能够将复杂的事情简单化,并将咱们从执行者变成了指挥者。面向对象的语言中,包含了三大基本特征,即封装、继承和多态。
类 :是一组相关属性和行为的集合。能够当作是一类事物的模板,使用事物的属性特征和行为特征来描述该
类事物。
类与对象的关系
定义格式:
public class ClassName { //成员变量 //成员方法 }
示例:
public class Student { //成员变量 String name;//姓名 int age;//年龄 //成员方法 //学习的方法 public void study() { System.out.println("好好学习,每天向上"); } //吃饭的方法 publicvoid eat() { System.out.println("学习饿了要吃饭"); } }
对象的使用格式
建立对象:
类名 对象名 = new 类名();
使用对象访问类中的成员:
对象名.成员变量; 对象名.成员方法();
示例:
public class Test01_Student { public static void main(String[] args) { //建立对象格式:类名 对象名 = new 类名(); Student s = new Student(); System.out.println("s:"+s); //cn.itcast.Student@100363 //直接输出成员变量值 System.out.println("姓名:"+s.name); //null System.out.println("年龄:"+s.age); //0 System.out.println("‐‐‐‐‐‐‐‐‐‐"); //给成员变量赋值 s.name = "赵丽颖"; s.age = 18; //再次输出成员变量的值 System.out.println("姓名:"+s.name); //赵丽颖 System.out.println("年龄:"+s.age); //18 System.out.println("‐‐‐‐‐‐‐‐‐‐"); //调用成员方法 s.study(); // "好好学习,每天向上" s.eat(); // "学习饿了要吃饭" } }
成员变量的默认值
基本类型:
0
0.0
'\u0000'
布尔(boolean):false
null
public class Car{ String color; //成员变量 public void drive(){ int speed = 80; //局部变量 //...... } }
在类中的位置不一样
做用范围不同
初始化值的不一样
存中的位置不一样
生命周期不一样
面向对象编程语言是对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的,外界没法直接操做和修改。封装能够被认为是一个保护屏障,防止该类的代码和数据被其余类随意访问。要访问该类的数据,必须经过指定的方式。适当的封装可让代码更容易理解与维护,也增强了代码的安全性。
封装的步骤
private的含义
private
是一个权限修饰符,表明最小权限。private
修饰后的成员变量和成员方法,只在本类中才能访问。private的使用格式:
private 数据类型 变量名;
public class Student { private String name; private int age; }
public class Student { private String name; private int age; public void setName(String n) { name = n; } public String getName() { return name; } public void setAge(int a) { age = a; } public int getAge() { return age; } }
this的含义this表明所在类的当前对象的引用(地址值),即对象本身的引用。
方法被哪一个对象调用,方法中的this
就表明那个对象。即谁在调用,this就表明谁。
this使用格式:
this.成员变量名;
使用 this 修饰方法中的变量,解决成员变量被隐藏的问题,代码以下:
因为形参变量名与成员变量名重名,致使成员变量名被隐藏,方法中的变量名,没法访问到成员变量,从而赋值失败。因此,咱们只能使用this关键字,来解决这个重名问题。
public class Student { private String name; private int age; public void setName(String name) { //name = name; this.name = name; } public String getName() { return name; } public void setAge(int age) { //age = age; this.age = age; } public int getAge() { return age; }
当一个对象被建立时候,构造方法用来初始化该对象,给对象的成员变量赋初始值。
不管你与否自定义构造方法,全部的类都有构造方法,由于Java自动提供了一个无参数构造方法,一旦本身定义了构造方法,Java自动提供的默认无参数构造方法就会失效。
构造方法的定义格式:
修饰符 构造方法名(参数列表){ // 方法体 }
构造方法的写法上,方法名与它所在的类名相同。它没有返回值,因此不须要返回值类型,甚至不须要void。使用构造方法后,代码以下:
public class Student { private String name; private int age; // 无参数构造方法 public Student() {} // 有参数构造方法 public Student(String name,int age) { this.name = name; this.age = age; } }
注意事项
JavaBean 是 Java语言编写类的一种标准规范。符合 JavaBean 的类,要求类必须是具体的和公共的,而且具备无参数的构造方法,提供用来操做成员变量的 set 和 get 方法。
public class ClassName{ //成员变量 //构造方法 //无参构造方法【必须】 //有参构造方法【建议】 //成员方法 //getXxx() //setXxx() }
编写符合 JavaBean 规范的类,以学生类为例,标准代码以下:
public class Student { //成员变量 private String name; private int age; //构造方法 public Student() {} public Student(String name,int age) { this.name = name; this.age = age; } //成员方法 publicvoid setName(String name) { this.name = name; } public String getName() { return name; } publicvoid setAge(int age) { this.age = age; } publicint getAge() { return age; } }
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那一个类便可。
其中,多个类能够称为子类,单独那一个类称为父类、超类(superclass)或者基类。
父类更通用,子类更具体。咱们经过继承,可使多种事物之间造成一种关系体系。
继承 :就是子类继承父类的属性和行为,使得子类对象具备与父类相同的属性、相同的行为。子类能够直接访问父类中的非私有的属性和行为。
好处:
格式:
经过 extends
关键字,能够声明一个子类继承另一个父类,定义格式以下:
class 父类 { ... } class 子类 extends 父类 { ... }
示例:
/* * 定义员工类Employee,作为父类 */ class Employee { String name; // 定义name属性 // 定义员工的工做方法 public void work() { System.out.println("全力以赴地工做"); } } /* * 定义讲师类Teacher 继承 员工类Employee */ class Teacher extends Employee { // 定义一个打印name的方法 public void printName() { System.out.println("name=" + name); } } /* * 定义测试类 */ public class ExtendDemo01 { public static void main(String[] args) { // 建立一个讲师类对象 Teacher t = new Teacher(); // 为该员工类的name属性进行赋值 t.name = "小明"; // 调用该员工的printName()方法 t.printName(); // name = 小明 // 调用Teacher类继承来的work()方法 t.work(); // 全力以赴地工做 } }
成员变量不重名
成员变量重名
super
关键字,修饰父类成员变量,相似于以前学过的 this
。成员方法不重名
成员方法重名 ——重写(Override)
class Fu { private int n; Fu(){ System.out.println("Fu()"); } } class Zi extends Fu { Zi(){ // super(),调用父类构造方法 super(); System.out.println("Zi()"); } } public class ExtendsDemo07{ public static void main (String args[]){ Zi zi = new Zi(); } } 输出结果: Fu() Zi()
Java只支持单继承,不支持多继承。
方法重写 :子类中出现与父类如出一辙的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效果,也称为重写或者复写。声明不变,从新实现。
示例:
子类能够根据须要,定义特定于本身的行为。既沿袭了父类的功能名称,又根据子类的须要从新实现父类方法,从而进行扩展加强。好比新的手机增长来电显示头像的功能,代码以下:
class Phone { public void sendMessage(){ System.out.println("发短信"); } public void call(){ System.out.println("打电话"); } public void showNum(){ System.out.println("来电显示号码"); } } //智能手机类 class NewPhone extends Phone { //重写父类的来电显示号码功能,并增长本身的显示姓名和图片功能 public void showNum(){ //调用父类已经存在的功能使用super super.showNum(); //增长本身特有显示姓名和图片功能 System.out.println("显示来电姓名"); System.out.println("显示头像"); } } public class ExtendsDemo06 { public static void main(String[] args) { // 建立子类对象 NewPhone np = new NewPhone(); // 调用父类继承而来的方法 np.call(); // 调用子类重写的方法 np.showNum(); } }
注意事项
父类空间优先于子类对象产生
在每次建立子类对象时,先初始化父类空间,再建立其子类对象自己。目的在于子类对象中包含了其对应的父类空间,即可以包含其父类的成员,若是父类成员非private修饰,则子类能够随意使用父类成员。代码体如今子类的构造方法调用时,必定先调用父类的构造方法。
super和this的含义
super和this的用法
this.成员变量 ‐‐ 本类的 super.成员变量 ‐‐ 父类的 this.成员方法名() ‐‐ 本类的 super.成员方法名() ‐‐ 父类的
示例:
class Animal { public void eat() { System.out.println("animal : eat"); } } class Cat extends Animal { public void eat() { System.out.println("cat : eat"); } public void eatTest() { this.eat(); // this 调用本类的方法 super.eat(); // super 调用父类的方法 } } public class ExtendsDemo { public static void main(String[] args) { Animal a = new Animal(); a.eat(); Cat c = new Cat(); c.eatTest(); } } 输出结果为: animal : eat cat : eat animal : eat
this(...) ‐‐ 本类的构造方法 super(...) ‐‐ 父类的构造方法
父类中的方法,被它的子类们重写,子类各自的实现都不尽相同。那么父类的方法声明和方法主体,只有声明还有意义,而方法主体则没有存在的意义了。咱们把没有方法主体的方法称为抽象方法。Java语法规定,包含抽象方法的类就是抽象类。
使用 abstract 关键字修饰方法,该方法就成了抽象方法,抽象方法只包含一个方法名,而没有方法体
格式:
修饰符 abstract 返回值类型 方法名 (参数列表);
示例:
public abstract void run();
格式:
abstract class 类名字 { }
示例:
public abstract class Animal { public abstract void run(); }
抽象的使用:
继承抽象类的子类必须重写父类全部的抽象方法。不然,该子类也必须声明为抽象类。最终,必须有子类实现该父类的抽象方法,不然,从最初的父类到最终的子类都不能建立对象,失去意义。
public class Cat extends Animal { public void run (){ System.out.println("小猫在墙头走~~~"); } } public class CatTest { public static void main(String[] args) { // 建立子类对象 Cat c = new Cat(); // 调用run方法 c.run(); } } 输出结果: 小猫在墙头走~~~
此时的方法重写,是子类对父类抽象方法的完成实现,咱们将这种方法重写的操做,也叫作实现方法。
多态 : 是指同一行为,具备多个不一样表现形式。
多态体现的格式:
父类类型 变量名 = new 子类对象; //父类类型:指子类对象继承的父类类型,或者实现的父接口类型。 变量名.方法名();
当使用多态方式调用方法时,首先检查父类中是否有该方法,若是没有,则编译错误;若是有,执行的是子类重写后方法。
定义父类:
public abstract class Animal { public abstract void eat(); }
定义子类:
class Cat extends Animal { public void eat() { System.out.println("吃鱼"); } } class Dog extends Animal { public void eat() { System.out.println("吃骨头"); } }
定义测试类:
public class Test { public static void main(String[] args) { // 多态形式,建立对象 Animal a1 = new Cat(); // 调用的是 Cat 的 eat a1.eat(); // 多态形式,建立对象 Animal a2 = new Dog(); // 调用的是 Dog 的 eat a2.eat(); } }
实际开发的过程当中,父类类型做为方法形式参数,传递子类对象给方法,进行方法的调用,更能体现出多态的扩展性与便利。
定义父类:
public abstract class Animal { public abstract void eat(); }
定义子类:
class Cat extends Animal { public void eat() { System.out.println("吃鱼"); } } class Dog extends Animal { public void eat() { System.out.println("吃骨头"); } }
定义测试类:
public class Test { public static void main(String[] args) { // 多态形式,建立对象 Cat c = new Cat(); Dog d = new Dog(); // 调用showCatEat showCatEat(c); // 调用showDogEat showDogEat(d); /* 以上两个方法, 都可以被showAnimalEat(Animal a)方法所替代 而执行效果一致 */ showAnimalEat(c); showAnimalEat(d); } public static void showCatEat (Cat c){ c.eat(); } public static void showDogEat (Dog d){ d.eat(); } public static void showAnimalEat (Animal a){ a.eat(); } }
因为多态特性的支持, showAnimalEat方法的Animal类型,是Cat和Dog的父类类型,父类类型接收子类对象,固然能够把Cat对象和Dog对象,传递给方法。
当eat方法执行时,多态规定,执行的是子类重写的方法,那么效果天然与showCatEat、showDogEat方法一致,因此showAnimalEat彻底能够替代以上两方法。
不只仅是替代,在扩展性方面,不管以后再多的子类出现,咱们都不须要编写showXxxEat方法了,直接使用showAnimalEat均可以完成。
因此,多态的好处,体如今,可使程序编写的更简单,并有良好的扩展。
多态的转型分为向上转型与向下转型两种
向上转型 :多态自己是子类类型向父类类型向上转换的过程,这个过程是默认的。当父类引用指向一个子类对象时,即是向上转型。
使用格式:
父类类型 变量名 = new 子类类型(); 如:Animal a = new Cat();
向下转型 :父类类型向子类类型向下转换的过程,这个过程是强制的。一个已经向上转型的子类对象,将父类引用转为子类引用,可使用强制类型转换的格式,即是向下转型。
使用格式:
子类类型 变量名 = (子类类型) 父类变量名; 如:Cat c =(Cat) a;
当使用多态方式调用方法时,首先检查父类中是否有该方法,若是没有,则编译错误。也就是说,不能调用子类拥有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给咱们带来的一点"小麻烦"。因此,想要调用子类特有的方法,必须作向下转型。
转型演示,代码以下:
定义类:
abstract class Animal { abstract void eat(); } class Cat extends Animal { public void eat() { System.out.println("吃鱼"); } public void catchMouse() { System.out.println("抓老鼠"); } } class Dog extends Animal { public void eat() { System.out.println("吃骨头"); } public void watchHouse() { System.out.println("看家"); } }
定义测试类:
public class Test { public static void main(String[] args) { // 向上转型 Animal a = new Cat(); a.eat(); // 调用的是 Cat 的 eat // 向下转型 Cat c = (Cat)a; c.catchMouse(); // 调用的是 Cat 的 catchMouse } }
转型的过程当中,一不当心就会遇到这样的问题,请看以下代码:
public class Test { public static void main(String[] args) { // 向上转型 Animal a = new Cat(); a.eat(); // 调用的是 Cat 的 eat // 向下转型 Dog d = (Dog)a; d.watchHouse(); // 调用的是 Dog 的 watchHouse 【运行报错】 } }
这段代码能够经过编译,可是运行时,却报出了 ClassCastException
,类型转换异常!这是由于,明明建立了Cat类型对象,运行时,固然不能转换成Dog对象的。这两个类型并无任何继承关系,不符合类型转换的定义。
为了不ClassCastException
的发生,Java提供了 instanceof
关键字,给引用变量作类型的校验,格式以下:
变量名 instanceof 数据类型 若是变量属于该数据类型,返回true。 若是变量不属于该数据类型,返回false。
因此,转换前,咱们最好先作一个判断,代码以下:
public class Test { public static void main(String[] args) { // 向上转型 Animal a = new Cat(); a.eat(); // 调用的是 Cat 的 eat // 向下转型 if (a instanceof Cat){ Cat c = (Cat)a; c.catchMouse(); // 调用的是 Cat 的 catchMouse } else if (a instanceof Dog){ Dog d = (Dog)a; d.watchHouse(); // 调用的是 Dog 的 watchHouse } } }