一、类、对象:
- 什么是类?什么是对象?
- 现实生活中是由很多很多对象组成的,基于对象抽出了类
- 对象:软件中真实存在的单个个体/东西
类:类型/类别,代表一类个体 - 类是对象的模板/模子,对象是类的具体的实例
- 类中可以包含:
- 对象的属性/特征-----------------------------成员变量
- 对象的行为/动作/功能----------------------方法
- 一个类可以创建多个对象
- 如何创建类?如何创建对象?如何访问成员?
package ooday01;
//学生类
public class Student {
//成员变量----对象的属性
String name;
int age;
String address;
//方法-----对象的行为/功能
void study(){
System.out.println(name+"在学习...");
}
void sayHi(){
System.out.println("大家好,我叫"+name+",今年"+age+"岁了,家住"+address);
}
}
package ooday01;
//学生类的测试类
public class StudentTest {
public static void main(String[] args) {
//创建一个学生对象
Student zs = new Student();
//给成员变量赋值
zs.name = "zhangsan";
zs.age = 18;
zs.address = "河北廊坊";
//调用方法
zs.study();
zs.sayHi();
Student ls = new Student();
ls.name = "lisi";
ls.age = 25;
ls.address = "黑龙江佳木斯";
ls.study();
ls.sayHi();
//1)创建了一个学生对象
//2)给所有成员变量赋默认值
Student ww = new Student();
ww.study();
ww.sayHi();
}
}
-
方法的签名:方法名+参数列表
-
方法的重载(overload/overloading):-------------------------------方便用户的调用
- 发生在同一类中,方法名相同,参数列表不同
public class Aoo{ void show(){} void show(String name){} void show(int age){} void show(String name,int age){} void show(int age,String name){} //int show(){ return 1; } //编译错误,重载与返回值类型无关 //void show(String address){} //编译错误,重载与参数名称无关 }
- 编译器在编译时会根据方法的签名自动绑定方法
public class OverloadDemo { public static void main(String[] args) { Aoo o = new Aoo(); o.show(); o.show("zhangsan"); o.show(25); o.show("zhangsan",25); o.show(25,"zhangsan"); } }
知识补充
- OO:面向对象
OOA:面向对象分析
OOD:面向对象设计
OOP:面向对象编程--------------------------你们以后所参与的 - 高质量的代码:------------------------想拿年薪
- 复用性好、扩展性好、维护性好、移植性好、健壮性好、可读性好、效率好…
- 类:是一种引用数据类型,是我们自己创造的一种数据类型
引用
数据类型 引用类型变量 指向 对象
Student zs = new Student();
//读作:声明一个Student类型的引用zs,指向了一个学生对象
- 默认值:
byte,short,int,long,char-----------------0
float,double-----------------------------0.0
boolean----------------------------------false
引用类型----------------------------------null
二、构造方法、关键字this:
-
构造方法:构造函数、构造器、构建器-----------------------复用给成员变量赋初值的代码
- 作用:给成员变量赋初值
- 与类同名,没有返回值类型(连void都没有)
- 在创建(new)对象时被自动调用
- 若自己不写构造方法,编译器默认提供一个无参构造方法,若自己写了构造方法,则不再默认提供
- 构造方法可以重载
public class Student { String name; //成员变量(整个类中) int age; String address; //给成员变量赋初值 Student(String name,int age,String address){ //局部变量(当前方法中) this.name = name; this.age = age; this.address = address; } void sayHi(){ System.out.println("大家好,我叫"+name+",今年"+age+"岁了,家住"+address); } } public class ConsDemo { public static void main(String[] args) { //Student zs = new Student(); //编译错误,Student类没有无参构造方法 Student zs = new Student("zhangsan",25,"LF"); zs.sayHi(); Student ls = new Student("lisi",24,"JMS"); ls.sayHi(); } }
-
this:指代当前对象,哪个对象调用方法它指的就是哪个对象
只能用在方法中,方法中访问成员变量之前默认有个this.
this的用法:- this.成员变量名---------------------------访问成员变量
当成员变量与局部变量同名时,若想访问成员变量,则this不能省略
- this.方法名()--------------------------------调用方法(一般不用)
- this()------------------------------------------调用构造方法(一般不用)
- this.成员变量名---------------------------访问成员变量
-
null:表示空,没有指向任何对象。
- 若引用的值为null,则该引用不能进行任何点操作了,若操作则发生NullPointerException空指针异常。
知识补充
- java规定:成员变量和局部变量是可以同名的,使用的时候默认采取的是就近原则
- 构造方法到底要不要参数,要看对象的数据能不能写死
如果对象的数据都一样,意味着可以写死,就不需要传参。
如果对象的数据都不一样,意味着不能写死,那就需要传参。 - 内存管理:由JVM来管理的
- 堆:new出来的对象(包括成员变量)
- 栈:局部变量(包括方法的参数)
- 方法区:
- null只是相对于引用类型而言,它与基本类型之间一点关系都没有
Student zs = null;
int a = null; //编译错误
class Aoo{
int a; //成员变量
void show(int b){ //局部变量
int c = 5;
}
}
new Aoo(); //--------堆中
a //-----------------堆中
b //-----------------栈中
c //----------------栈中
-
方向图
-
堆栈内存图
-
null和NullPointerException
三、引用数组类型、继承、关键字super:
-
引用类型数组:
- 给元素赋值时,需要new个对象
- 若想访问对象的数据,需要通过数组元素去打点来访问
//对于引用类型数组而言,必须给每个元素都赋值的 //若元素不赋值,则默认值为null,容易发生空指针异常 Student[] stus = new Student[3]; //创建Student数组对象 stus[0] = new Student("zhangsan",25,"LF"); //创建Student对象 stus[1] = new Student("lisi",24,"JMS"); stus[2] = new Student("wangwu",26,"SD"); System.out.println(stus[0].name); //输出第1个学生的名字 stus[1].age = 27; //修改第2个学生的年龄为27 stus[2].sayHi(); //第3个学生跟大家问好 for(int i=0;i<stus.length;i++){ //遍历所有学生 System.out.println(stus[i].name); //输出每个学生的名字 stus[i].sayHi(); //每个学生跟大家问好 } Student[] stus = new Student[]{ new Student("zhangsan",25,"LF"), new Student("lisi",24,"JMS"), new Student("wangwu",26,"SD") };
-
继承:
-
作用:代码复用
-
通过extends来实现继承
-
超类/父类:共有的属性和行为
派生类/子类:特有的属性和行为 -
派生类可以访问派生类的+超类的,但超类不能访问派生类的
-
一个超类可以有多个派生类,但一个派生类只能继承一个超类-----------单一继承
-
继承具有传递性
-
java规定:构造派生类之前必须先构造超类
- 在派生类的构造方法中若没有调用超类的构造方法,则默认super()调用超类的无参构造方法
public class SuperDemo { public static void main(String[] args) { Boo o = new Boo(); } } class Aoo{ Aoo(){ System.out.println("超类构造方法"); } } class Boo extends Aoo{ Boo(){ super(); //默认的,调用超类的无参构造方法 System.out.println("派生类构造方法"); } }
-
在派生类的构造方法中若自己调用了超类的构造方法,则不再默认提供
注意:super()调用超类构造方法,必须位于派生类构造方法的第一行
class Coo{ Coo(int a){ } } class Doo extends Coo{ Doo(){ super(5); //调用Coo的有参构造方法 } /* //如下代码为默认的 Doo(){ super(); } */ }
-
-
super:指代当前对象的超类对象
super的用法:
- super.成员变量名-------------------------访问超类的成员变量
- super.方法名()-----------------------------调用超类的方法
- super()---------------------------------------调用超类的构造方法
补充知识
- 基本数组类型内存图
- 引用型数组类型内存图
四、向上造型、重写与重载:
-
向上造型:--------------------------------------------代码复用
- 超类型的引用指向派生类的对象
- 能点出来什么,看引用的类型----------------这是规定,记住就OK
何时向上造型:
-
多种角色能干的事都一样的时候,可以将多种角色统一造型到超类数组中,实现代码复用
eg: 学生/老师/医生都是输出名字+问好------干的事都一样,
就可以将学生/老师/医生统一造型到Person数组中,这样一个for即可-------代码复用
-
public class UploadDemo { public static void main(String[] args) { Aoo o1 = new Aoo(); o1.a = 1; o1.show(); //o1.b = 2; //编译错误 //o1.test(); //编译错误,超类不能访问派生类的 Boo o2 = new Boo(); o2.b = 1; o2.test(); o2.a = 2; //正确 o2.show(); //正确,派生类可以访问超类的 Aoo o3 = new Boo(); //向上造型 o3.a = 1; o3.show(); //o3.b = 2; //编译错误 //o3.test(); //编译错误,能点出来什么,看引用的类型 } } class Aoo{ int a; void show(){ } } class Boo extends Aoo{ int b; void test(){ } }
-
方法的重写(override/overriding):重新写、覆盖
- 发生在父子类中,方法名相同,参数列表相同
- 重写方法被调用时,看对象的类型-----------------------这是规定,记住就OK
class 餐馆{ void 做餐(){ 做中餐 } } //1)我还是想做中餐-----------------不需要重写 class Aoo extends 餐馆{ } //2)我想改做西餐-------------------需要重写 class Aoo extends 餐馆{ void 做餐(){ 做西餐 } } //3)我想在中餐基础之上加入西餐--------需要重写(先super中餐,再加入西餐) class Aoo extends 餐馆{ void 做餐(){ super.做餐(); 做西餐 } }
- 重写需遵循"两同两小一大"原则:-------------------------了解,一般都是一模一样的
- 两同:
- 方法名相同
- 参数列表相同
- 两小:
-
派生类方法的返回值类型小于或等于超类方法的
- void和基本类型时,必须相等
- 引用类型时,小于或等于
//超类大,派生类小---------爸爸大,儿子小 class Coo{ void show(){} double test(){ return 0.0; } Student say(){ return null; } Person sayHi(){ return null; } } class Doo extends Coo{ //int show(){ return 1; } //编译错误,void时必须相等 //int test(){ return 0; } //编译错误,基本类型时必须相等 //Person say(){ return null; } //编译错误,引用类型时必须小于或等于 Student sayHi(){ return null; } //正确,Student小于Person }
-
派生类方法抛出的异常小于或等于超类方法的
-
- 一大:
- 派生类方法的访问权限大于或等于超类方法的-
- 两同:
-
重写与重载的区别:----------常见面试题
- 重写:发生在父子类中,方法名相同,参数列表相同
- 重载:发生在同一类中,方法名相同,参数列表不同
补充知识
- 继承要符合is(是)的关系
- 超类的意义:
- 封装共有的属性和行为----------------------实现代码复用
- 为所有派生类提供了统一的类型----------向上造型(实现代码复用)
五、Package、访问控制修饰符、静态方法:
-
package:声明包
- 作用:避免类的命名冲突
- 同包中的类不能同名,但不同包中的类可以同名
- 类的全称:包名.类名,包名常常有层次结构
- 建议:包名所有字母都小写
-
import:导入类
同包中的类可以直接访问
不同包中的类不能直接访问,若想访问:
先import导入类再使用类------------建议
类的全称----------------------------------太繁琐,不建议 -
访问控制修饰符:------------------保护数据的安全
-
public:公开的,任何类
-
private:私有的,本类
-
protected:受保护的,本类、派生类、同包类
-
默认的:什么也不写,本类、同包类
说明:
- java不建议默认访问权限
- 类的访问权限只能是public或默认的,类中成员的访问权限如上4种都可以
- 访问权限由小到大依次为:private<默认的<protected<public
class Card{ //银行卡 private String cardId; //卡号 private String cardPwd; //密码 private double balance; //余额 public boolean payMoney(double money){ //支付金额---收银员可以调用 if(balance>=money){ balance-=money; return true; }else{ return false; } } public boolean checkPwd(String pwd){ //检测密码--营业员可以调用 if(pwd和cardPwd相同){ return true; }else{ return false; } } }
//访问控制符的演示 public class Aoo { public int a; //任何类 protected int b; //本类、派生类、同包类 int c; //本类、同包类 private int d; //本类 void show(){ a = 1; b = 2; c = 3; d = 4; } } class Boo{ //---------------------演示private void show(){ Aoo o = new Aoo(); o.a = 1; o.b = 2; o.c = 3; //o.d = 4; //编译错误 } } package ooday05_vis; import ooday05.Aoo; public class Coo { //---------------演示同包 void show(){ Aoo o = new Aoo(); o.a = 1; //o.b = 2; //编译错误 //o.c = 3; //编译错误 //o.d = 4; //编译错误 } } class Doo extends Aoo{ //跨包继承---------演示protected void show(){ a = 1; b = 2; //c = 3; //编译错误 //d = 4; //编译错误 } }
-
-
final:最终的、不能改变的-----------------单独应用几率低
-
修饰变量:变量不能被改变
class Eoo{ int a = 5; final int b = 5; void show(){ a = 55; //b = 55; //编译错误,final修饰的变量,不能被改变 } }
-
修饰方法:方法不能被重写
class Foo{ void show(){} final void test(){} } class Goo extends Foo{ void show(){} //void test(){} //编译错误,final修饰的方法,不能被重写 }
-
修饰类:类不能被继承
final class Hoo{} //class Ioo extends Hoo{} //编译错误,final的类不能被继承 class Joo{} final class Koo extends Joo{} //正确,不能当老爸,但能当儿子
-
-
static:静态的
-
静态变量:
- 由static修饰
- 属于类,存储在方法区中,一份
- 常常通过类名点来访问
- 何时用:所有对象所共享的数据(图片、音频、视频等)
//演示静态变量 class Loo{ int a; static int b; Loo(){ a++; b++; } void show(){ System.out.println("a="+a+",b="+b); } } public class StaticDemo { public static void main(String[] args) { Loo o1 = new Loo(); o1.show(); Loo o2 = new Loo(); o2.show(); Loo o3 = new Loo(); o3.show(); System.out.println(Loo.b); //常常通过类名点来访问 } }
-
静态方法:
- 由static修饰
- 属于类,存储在方法区中,一份
- 常常通过类名点来访问
- 静态方法中没有隐式this传递,所以只能直接访问静态成员,而不能直接访问实例成员
- 何时用:方法的操作与对象无关----方法中不需要访问对象的属性或行为
//演示静态方法 class Moo{ int a; //实例变量(对象来访问) static int b; //静态变量(类名来访问) void show(){ //有隐式this System.out.println(this.a); System.out.println(Moo.b); } static void test(){ //没有隐式this //静态方法中没有隐式this传递 //没有this就意味着没有对象 //而实例成员a必须通过对象来访问 //所以如下语句会发生编译错误 //System.out.println(a); //编译错误 System.out.println(Moo.b); } } //演示静态方法的应用场景 class Noo{ int a; //实例变量---描述对象的属性 //show()方法中需要访问对象的属性a,说明show()的操作与对象有关---不能静态方法 void show(){ System.out.println(a); } //plus()方法中不需要访问对象的属性和行为,说明plus()的操作与对象无关-可以静态方法 static void plus(int num1,int num2){ int num=num1+num2; System.out.println(num); } } public class StaticDemo { public static void main(String[] args) { Noo.plus(5,7); //常常通过类名点来访问 } }
-
静态块:
- 由static修饰
- 属于类,在类被加载时自动执行,因为一个类只被加载一次,所以静态块也只执行一次
- 何时用:加载/初始化静态资源(图片、音频、视频等)
//演示静态块 class Poo{ static{ System.out.println("静态块"); } Poo(){ System.out.println("构造方法"); } } public class StaticDemo { public static void main(String[] args) { Poo o4 = new Poo(); Poo o5 = new Poo(); } }
-
补充知识
-
访问权限常规设置模式:
- 数据(成员变量)私有化(private)
- 行为(方法)公开化(public)------------------绝大部分方法都是公开的
-
成员变量分两种:
- 实例变量:没有static修饰,属于对象的,存储在堆中,有几个对象就有几份
通过对象/引用打点来访问 - 静态变量:有static修饰,属于类的,存储在方法区中,只有一份
常常通过类名点来访问
- 实例变量:没有static修饰,属于对象的,存储在堆中,有几个对象就有几份
-
内存管理:由JVM来管理的
- 堆:new出来的对象(包括实例变量、数组的元素)
- 栈:局部变量(包括方法的参数)
- 方法区:.class字节码文件(包括静态变量、所有方法)
-
图片:
// 公开的 静态的 图片类型 变量名 public static ImageIcon sea;
-
静态变量内存图
六、抽象方法、抽象类:
- static final常量:应用率高
- 必须声明同时初始化
- 由类名打点来访问,不能被改变
- 建议:常量所有字母都大写,多个单词用_分隔
- 编译器在编译时会将常量直接替换为具体的数,效率高
- 何时用:数据永远不变,并且经常使用
- 抽象方法:
- 由abstract修饰
- 只有方法的定义,没有具体的实现(连{}都没有)
- 抽象类:
- 由abstract修饰
- 包含抽象方法的类必须是抽象类
- 抽象类不能被实例化(new对象)
- 抽象类是需要被继承的,派生类:
- 重写抽象方法---------------变不完整为完整
- 也声明为抽象类------------一般不这么用
- 抽象类的意义:
- 封装共有的属性和行为-------------------代码复用
- 为所有派生类提供统一的类型----------向上造型(代码复用)
- 可以包含抽象方法,为所有派生类提供统一的入口(向上造型后能点出来),同时可以达到强制必须重写的目的(相当于制定了一个标准)
补充知识
- 设计规则:
- 将派生类所共有的属性和行为,抽到超类中-------------抽共性
- 若派生类的行为(实现代码)都一样,设计为普通方法
若派生类的行为(实现代码)都不一样,设计为抽象方法
- 抽象方法/抽象类的疑问:
- 抽象方法存在的意义是什么?
- 保证当发生向上造型时,通过超类的引用能点出来那个方法-------保证能点出方法来
- 既然抽象方法的意义是保证能点出来,那为什么不设计为普通方法呢?
- 设计为普通方法,意味着派生类可以重写也可以不重写,但设计为抽象方法,则可以强制派生类必须重写--------达到强制派生类重写,统一的目的
- 抽象方法存在的意义是什么?
七、成员类部类、匿名类部类
-
成员类部类:
- 类中套类,外面的称为外部类,里面的称为内部类
- 内部类通常只服务于外部类,对外不具备可见性
- 内部类对象通常在外部类中创建
- 内部类中可以直接访问外部类的成员(包括私有的)
- 内部类中有一个隐式的引用,指向了创建它的外部类对象
-----------------------------------------外部类名.this
- 内部类中有一个隐式的引用,指向了创建它的外部类对象
public class InnerClassDemo {
public static void main(String[] args) {
Mama m = new Mama();
//Baby b = new Baby(); //编译错误,内部类对外不具备可见性
}
}
class Mama{ //外部类
private String name;
void create(){
Baby b = new Baby();
}
class Baby{ //内部类
void showName(){
System.out.println(name); //简便写法
System.out.println(Mama.this.name); //完整写法
//System.out.println(this.name); //编译错误,this指代当前Baby对象
}
}
}
- 匿名内部类:----------大大简化代码的操作
- 若想创建一个类(派生类)的对象,并且对象只被创建一次,此时可以设计为匿名内部类
- 在匿名内部类中不能修饰外面局部变量的值,因为该变量在此处会默认为final的
- 问:内部类有独立的.class吗?
答:有
public class NstInnerClassDemo {
public static void main(String[] args) {
//1)系统自动创建了Aoo的一个派生类,但是没有名字
//2)为该派生类创建了一个对象,名为o1
// ---new Aoo(){}是在创建Aoo的派生类的对象
//3)大括号中的为派生类的类体
Aoo o1 = new Aoo(){
};
//1)系统自动创建了Aoo的一个派生类,但是没有名字
//2)为该派生类创建了一个对象,名为o2
//3)大括号中的为派生类的类体
Aoo o2 = new Aoo(){
};
//1)系统自动创建了Boo的一个派生类,但是没有名字
//2)为该派生类创建了一个对象,名为o3
//3)大括号中的为派生类的类体
Boo o3 = new Boo(){
void show(){
System.out.println("showshow");
}
};
o3.show();
int num = 5;
num = 55;
Boo o4 = new Boo(){
void show(){
System.out.println("showshow");
//num = 66; //编译错误,匿名内部类中不能修改外面局部变量的值,
//因为在此处该变量会默认为final的
}
};
}
}
abstract class Boo{
abstract void show();
}
abstract class Aoo{
}
补充知识
- 隐式的引用:
- this:指代当前对象
- super:指代当前对象的超类对象
- 外部类名.this:指代当前对象的外部类对象
- 做功能的套路:
- 先写行为/方法:
- 若为派生类所特有的行为,则将方法设计在特定的类中
- 若为所有派生类所共有的行为,则将方法设计在超类中
- 窗口调用:
- 若为定时(自动)发生的,则在定时器中调用
- 若为事件触发的,则在侦听器中调用
- 先写行为/方法:
- 调用方法的注意事项:
- 1)若方法是有返回值的,则必须声明对应类型的变量来接收
- 调用时:
- 若与方法在同一个类中,则可以直接调用
- 若与方法不在同一个类中,则需要通过引用名打点来调用
- 若方法为静态的,则直接通过类名打点来调用
- 错误分类:
- 编译错误----都是由于违反语法规则了
- 异常--------运行时发生,找到at后的链接点击
- 程序的运行结果与你所预期的结果不同------------必须得调错
- 如何调错:
- 打桩: System.out.println(数据);
八、接口:
-
接口:
- 是一种数据类型(引用类型)
- 由interface定义
- 只能包含常量和抽象方法(所有数据默认都是常量,所有方法默认都是抽象的)
- 接口不能被实例化
- 接口是需要被实现/继承的,实现/派生类:必须重写所有抽象方法
- 一个类可以实现多个接口,用逗号分隔。若又继承又实现时,应先继承后实现。
- 接口可以继承接口
//接口的演示 public class InterfaceDemo { public static void main(String[] args) { //Inter5 o = new Inter5(); //编译错误,接口不能被实例化 Inter5 o1 = new Doo(); //向上造型 Inter4 o2 = new Doo(); //向上造型 } } //演示接口的语法 interface Inter{ public static final int NUM = 5; public abstract void show(); int COUNT = 5; //默认public static final void test(); //默认public abstract //int number; //编译错误,常量必须声明同时初始化 //void say(){ } //编译错误,抽象方法不能有方法体 } //演示接口的实现 interface Inter1{ void show(); void test(); } class Aoo implements Inter1{ public void show(){} //重写接口中的抽象方法时,必须加public权限 public void test(){} } //演示接口的多实现 interface Inter2{ void show(); } interface Inter3{ void test(); } abstract class Boo{ abstract void say(); } class Coo extends Boo implements Inter2,Inter3{ public void show(){} public void test(){} void say(){} } //演示接口继承接口 interface Inter4{ void show(); } interface Inter5 extends Inter4{ void test(); } class Doo implements Inter5{ public void test(){} public void show(){} }
补充知识
- 类中成员的默认访问权限---------默认的
接口中成员的默认访问权限------public的
重写接口中的抽象方法时,必须加public权限 - 类和类------------------------继承
接口和接口------------------继承
类和接口---------------------实现 - 能够造型成为的类型:超类+所实现的接口
- 设计规则:
- 将所有派生类所共有的属性和行为,抽到超类中-------------抽共性
- 若派生类的行为(实现代码)都一样,设计为普通方法
若派生类的行为(实现代码)都不一样,设计为抽象方法 - 将部分派生类所共有的属性和行为,抽到接口中
接口是对继承的单根性的扩展--------------实现多继承
接口是一个标准、一种规范,实现了接口就能干某件事,不实现接口就干不了那个事
- 如何调错:---------------------帮助我们找到问题位置所在
- 快速锁定问题方法:
- 将方法调用的代码全都注释掉,然后一个一个放开,
放开哪个出错了,说明问题就出在那个方法上。
- 将方法调用的代码全都注释掉,然后一个一个放开,
- 打桩: System.out.println(数据);
- 快速锁定问题方法:
九、多态:
-
多态:多种形态
- 同一个对象被造型为不同的类型时,有不同的功能-------所有对象都是多态的
- 对象的多态:水、我、你…
同一类型的引用在指向不同的对象时,有不同的实现----所有抽象方法都是多态的 - 行为的多态:cut()、getImage()、move()…
- 对象的多态:水、我、你…
- 向上造型/自动类型转换:
- 超类型的引用指向派生类的对象------前面是超类型,后面是派生类型
- 能点出来什么,看引用的类型
- 能造型成为的数据类型:超类+所实现的接口
- 强制类型转换,成功的条件只有两种:
- 引用所指向的对象,就是该类型
- 引用所指向的对象,实现了该接口或继承了该类
- 强转时若不满足如上条件,则发生ClassCastException类型转换异常
建议:在强转之前先通过instanceof来判断引用指向的对象是否是该类型说明:instanceof会返回true或false的结果
如果满足强转成功的条件则返回true,否则返回false
何时需要强转:向上造型后,若想访问的东西在超类中没有,则需要强转
public class MultiTypeDemo { public static void main(String[] args) { //成功的条件1:引用所指向的对象,就是该类型 //成功的条件2:引用所指向的对象,实现了该接口或继承了该类 Aoo o = new Boo(); //向上造型 Boo o1 = (Boo)o; //引用o所指向的对象,就是Boo类型-----------符合条件1 Inter o2 = (Inter)o; //引用o所指向的对象,实现了Inter接口---符合条件2 //Coo o3 = (Coo)o; //运行时会发生ClassCastException类型转换异常 if(o instanceof Coo){ //false Coo o4 = (Coo)o; }else{ System.out.println("o不是Coo类型"); } } } interface Inter{ } class Aoo{ } class Boo extends Aoo implements Inter{ } class Coo extends Aoo{ }
- 同一个对象被造型为不同的类型时,有不同的功能-------所有对象都是多态的
补充知识
- 体会接口的好处:
//复用性好、扩展性好、维护性好------------------高质量代码
//被撞的是ObserveSubmarine-----调用ObserveSubmarine的getScore()-----10分
//被撞的是TorpedoSubmarine-----调用TorpedoSubmarine的getScore()-----40分
//被撞的是NuclearSubmarine-----调用NuclearSubmarine的getScore()-----100分
if(s instanceof EnemyScore){ //------适用于所有实现EnemyScore接口的
EnemyScore es = (EnemyScore)s;
score += es.getScore();
}
//被撞的是MineSubmarine--------调用MineSubmarine的getLife()---------1
//被撞的是NuclearSubmarine-----调用NuclearSubmarine的getLife()------3
if(s instanceof EnemyLife){ //-------适用于所有实现EnemyLife接口的
EnemyLife el = (EnemyLife)s;
int num = el.getLife();
ship.addLife(num);
}
//复用性差、扩展性差、维护性差------------------垃圾代码
if(s instanceof ObserveSubmarine){ //---------只能适用于ObserveSubmarine的
ObserveSubmarine os = (ObserveSubmarine)s;
score += os.getScore();
}
if(s instanceof TorpedoSubmarine){ //---------只能适用于TorpedoSubmarine的
TorpedoSubmarine ts = (TorpedoSubmarine)s;
score += ts.getScore();
}
if(s instanceof MineSubmarine){ //---------只能适用于MineSubmarine的
MineSubmarine ms = (MineSubmarine)s;
int num = ms.getLife();
ship.addLife(num);
}
if(s instanceof NuclearSubmarine){ //---------只能适用于NuclearSubmarine
NuclearSubmarine ns = (NuclearSubmarine)s;
score += ns.getScore();
int num = ns.getLife();
ship.addLife(num);
}
十、内存管理、String、字符池常量:
-
内存管理:由JVM管理
- 堆:
- 存储new出来的对象(包括实例变量、数组的元素)
- 垃圾:没有任何引用所指向的对象
垃圾回收器(GC)不定时到内存堆中清扫垃圾,回收过程是透明的(看不到的),
不一定一发现垃圾就立刻回收,通过调用System.gc()可以建议虚拟机尽快调度GC来回收 - 实例变量的生命周期:
创建(new)对象时存储在堆中,对象被回收时一并被回收 - 内存泄漏:不再使用的对象还没有被及时的回收,严重的泄漏会导致系统的崩溃
建议:不再使用的对象应及时将引用设置为null
- 栈:
- 存储正在调用的方法中的局部变量(包括方法的参数)
- 调用方法时,会在栈中为该方法分配一块对应的栈帧,栈帧中存储局部变量(包括方法的参数),
方法调用结束时,栈帧被自动清除,局部变量一并被清除。 - 局部变量的生命周期:
调用方法时存储在栈中,方法结束时与栈帧一并被清除
- 方法区:
- 存储.class字节码文件(包括静态变量、所有方法)
- 方法只有一份,通过this来区分具体的调用对象
- 堆:
-
面向对象三大特征总结:
-
封装:
- 类:封装的是对象的属性和行为
- 方法:封装的是具体的业务逻辑功能实现
- 访问控制修饰符:封装的是具体的访问权限
-
继承:
- 作用:代码复用
- 超类:所有派生类所共有的属性和行为
接口:部分派生类所共有的属性和行为
派生类:派生类所特有的属性和行为 - 单一继承、多接口实现,具有传递性
-
多态:
- 所有对象都是多态的,通过向上造型来体现
所有抽象方法都是多态的,通过方法的重写来体现 - 向上造型、强制类型转换、instanceof判断
- 所有对象都是多态的,通过向上造型来体现
-
-
String字符串类型:
- java.lang.String类使用final修饰,不能被继承
- String的底层封装的是一个字符数组
- String在内存中采用Unicode编码格式,每个字符占用2个字节的空间
- 字符串对象一旦创建,对象内容永远无法改变,但字符串引用可以重新赋值(指向新的对象)
- 不变对象
-
字符串常量池:
- java对String字符串有一个优化措施:字符串常量池(堆中)
- java推荐我们使用字面量/直接量(直接"")的方式来创建对象,并且会缓存所有以字面量形式创建的字符串对象到常量池中,当使用相同字面量再创建对象时将会复用常量池中的对象,以减少内存开销
/* 使用字面量来创建字符串对象时,JVM会检查常量池中是否有该对象: 1)若没有,则会创建该字符串对象,并存入常量池中 2)若有,则直接将常量池中的对象(引用)返回---并不会创建新的字符串对象 */ String s1 = "123abc"; //常量池还没有,因此创建该字符串对象,并存入常量池中 String s2 = "123abc"; //常量池中已经有了,直接复用对象 String s3 = "123abc"; //常量池中已经有了,直接复用对象 //引用类型==,比较的是地址是否相同-----这是规定 System.out.println(s1==s2); //true System.out.println(s1==s3); //true System.out.println(s2==s3); //true s1 = s1 + "!"; //创建新的字符串对象(123abc!)并将地址赋值给s1 System.out.println(s1==s2); //false
补充知识
- 堆
- 栈
-
字符串常量池
-
方法区