继承、封装、多态
第1章类和对象
成员变量与局部变量区别
1、作用域不同
局部变量的作用域仅限于定义他的方法。
成员变量的作用域在整个类内部都是可见的
2、初始值不同
Java会给成员变量一个初始值(默认为0,String默认为null)
Java不会给局部变量赋予一个初始值(不赋值会提示报错)
3、在同一个方法中不允许有同名局部变量
在不同的方法中,可以有同名局部变量
4、两类变量同名时,局部变量具有更高的优先级
(就近原则—方法内部:局部变量会覆盖成员变量)
public class HelloWorld {
int a;
String str="成员变量";// 声明成员变量
public void asy() {
str="hello world";//相当于为成员变量赋值
//如果 成员变量str为final会很明显看出为其赋值时报错。
//String str="hello world";仅仅相当于声明了一个重名的局部变量而已。与成员变量的原str无关
System.out.println("asy()中str:"+str);
System.out.println("asy()中a:"+a);
}
public void asy2(){
//str="再次被修改"; 输出asy2()中str:再次被修改
System.out.println("asy2()中str:"+str);
System.out.println("asy2()中a:"+a);
}
public static void main(String[] args) {
HelloWorld hello = new HelloWorld();
hello.asy();
hello2.asy2();
}
}
//1:如果 成员变量a和str都不赋值的时候 输出a:0 str:null(成员变量会默认赋值)
//2:asy()方法中String str="hello world"; (局部变量具有更高的优先级)
输出:
asy()中str:hello world
asy()中a:10
asy2()中str:成员变量
asy2()中a:0
//3、如果 成员变量str在asy()中赋值str="hello world";则原成员变量的值被覆盖(重新指向)
asy()中str:hello world
asy()中a:10
asy2()中str:hello world
asy2()中a:0
构造方法
1.使用new+构造方法 创建一个新的对象(创建对象的时候,执行的是构造方法)
2.构造方法是定义在Java类中的一个用来初始化对象的方法
构造方法与类同名且没有返回值
//没有返回值
//
public 构造方法名(可以指定参数【是为了初始化成员变量,赋值】,也可以是无参的){//构造方法名与类名相同
//初始化代码
}
new+构造方法
public class Telphone {
private String name;
private float screen;
//当没有指定构造方法时,系统会自动添加无参的构造方法
//当有指定构造方法时,无论有参、无参的构造方法,将不会自动添加无参的构造方法
public Telphone(){
System.out.println("无参的构造方法");
}
//构造方法不但可以给对象的属性赋值,还可以保证给对象的属性赋一个合理的值
public Telphone(String band,float newScreen){
name=band;//给对象的属性赋值
if(newScreen<3.5f){
System.out.println("输入的屏幕尺寸过小,自动赋默认值3.5f");
screen=3.5f;
}else{
screen=newScreen;
}
System.out.println("有参的构造方法,入参name:"+name);
}
}
public static void main(String[] args) {
//通过无参的构造方法创建对象
Telphone phone=new Telphone();//会打印出:无参的构造方法
//通过有参的构造方法创建对象
Telphone phone2=new Telphone("华为",1.5f);//输出结果:
//输入的屏幕尺寸过小,自动赋默认值3.5f
//有参的构造方法,入参name:华为
}
构造方法的重载:
方法名相同
参数不同
调用时会根据不同的参数选择相应的方法。
static 使用之静态变量
Java 中被 static 修饰的成员称为静态成员或类成员。它属于整个类所有,而不是某个对象所有,即被类的所有对象所共享。静态成员可以使用类名直接访问,也可以使用对象名进行访问。
使用 static 可以修饰变量、方法和代码块。
输出结果
通过类名访问hobby:imooc
通过对象名访问hobby:imooc
通过类名访问hobby:"爱慕课"(被修改了,注意与final的区别)
静态成员属于整个类,当系统第一次使用该类时,就会为其分配内存空间直到该类被卸载才会进行资源回收!
静态方法
1、 静态方法中可以直接调用同类中的静态成员,但不能直接调用非静态成员。
如果希望在静态方法中调用非静态变量,可以通过创建类的对象,然后通过对象来访问非静态变量。
2、 在普通成员方法中,则可以直接访问同类的非静态变量和静态变量
3、 静态方法中不能直接调用非静态方法,需要通过对象来访问非静态方法。( 静态方法中能直接调用静态方法)
静态初始化块只在类加载时执行,且只会执行一次,同时静态初始化块只能给静态变量赋值,不能初始化普通的成员变量。
public class HelloWorld {
String name; // 声明变量name
String sex; // 声明变量sex
static int age;// 声明静态变量age
// 构造方法
public HelloWorld() {
System.out.println("通过构造方法初始化name");
name = "tom";
}
// 初始化块
{
System.out.println("通过初始化块初始化sex");
sex = "男";
}
// 静态初始化块
static {
System.out.println("通过静态初始化块初始化age");
age = 20;
}
public void show() {
System.out.println("姓名:" + name + ",性别:" + sex + ",年龄:" + age);
}
public static void main(String[] args) {
System.out.println("创建hello对象");
// 创建对象
HelloWorld hello = new HelloWorld();
// 调用对象的show方法
hello.show();
System.out.println("创建hello2对象");
HelloWorld hello2 = new HelloWorld();
hello2.show();
}
}
输出结果:
通过静态初始化块初始化age
创建hello对象
通过初始化块初始化sex
通过构造方法初始化name
姓名:tom,性别:男,年龄:20
创建hello2对象
通过初始化块初始化sex
通过构造方法初始化name
姓名:tom,性别:男,年龄:20
静态初始化块–>普通初始化块–>构造方法。
由于静态初始化块只在类加载时执行一次,所以当再次创建对象hello2 时并未执行静态初始化块。
封装
**1.概念:**将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问
2、好处:
a。只能通过规定的方法访问数据
b。隐藏类的实例细节,方便修改和实现
3、封装的实现步骤
修改属性的可见性——设为private
创建getter/setter方法——用于属性的读写
在getter/setter方法中加入属性控制语句——对属性值的合法性进行判断
访问修饰符
访问修饰符 | 本类 | 同包 | 子类 | 其他 |
---|---|---|---|---|
private | √ | |||
默认 | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
this关键字
this关键字代表当前对象
this.属性 表示当前对象的属性
this.方法 表示当前对象的方法
封装对象的属性的时候,常常会使用this关键字
**内部类( Inner Class )**就是定义在另外一个类里面的类。与之对应,包含内部类的类被称为外部类。
问:那为什么要将一个类定义在另一个类里面呢?清清爽爽的独立的一个类多好啊!!
答:内部类的主要作用如下:
- 内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类
- 内部类的方法可以直接访问外部类的所有数据,包括私有的数据
- 内部类所实现的功能使用外部类同样可以实现,只是有时使用内部类更方便
//外部类HelloWorld
public class HelloWorld {
// 内部类Inner,类Inner在类HelloWorld的内部
public class Inner {
// 内部类的方法
public void show() {
System.out.println("welcome to imooc!");
}
}
public static void main(String[] args) {
// 创建外部类对象
HelloWorld hello = new HelloWorld();
// 创建内部类对象
Inner i = hello.new Inner();
// 调用内部类对象的方法
i.show();
}
}
内部类可分为以下几种:
- 成员内部类 --也称为普通内部类
- 静态内部类
- 方法内部类
- 匿名内部类
成员内部类
//外部类Outer
public class Outer{
//外部类的私有属性name
private String name = "imooc";
//外部类的成员属性
int age = 20;
/*1、成员内部类Inner定义在 Outer 类的内部,相当于 Outer 类的一个成员变量的位置,
Inner 类可以使用任意访问控制符,如 public 、 protected 、 private 等*/
public class Inner {
String name = "爱慕课";
//内部类中的方法
public void show() {
/* 2、Inner 类中定义的show() 方法可以直接访问 Outer 类中的数据,而不受访问控制符的影响,
如直接访问 Outer 类中的私有属性age,name;*/
System.out.println("外部类中的age:" + age);
/*如果外部类和内部类具有相同的成员变量或方法,内部类默认访问自己的成员变量或方法,
如果要访问外部类的成员变量,可以使用 this 关键字。*/
System.out.println("外部类中的name:" +Outer.this.name);
System.out.println("内部类中的name:" + name);
}
}
//测试成员内部类
public static void main(String[] args) {
//创建外部类的对象
Outer o = new Outer ();
/*3、 定义了成员内部类后,必须使用外部类对象来创建内部类对象,而不能直接去 new 一个内部类对象,
即:【内部类 对象名 = 外部类对象.new 内部类( );】*/
Inner inn = o.new Inner();
//调用内部类对象的show方法
inn.show();
}
}
4、 编译上面的程序后,会发现产生了两个 .class 文件:
Out$Inner.class
Outer.class
其中,第二个是外部类的 .class 文件,第一个是内部类的 .class 文件,即成员内部类的 .class 文件总是这样:外部类名$内部类名.class
注意:
1、 外部类是不能直接使用内部类的成员和方法
可先创建内部类的对象,然后通过内部类的对象来访问其成员变量和方法。
静态内部类
static 修饰的内部类
//外部类
public class HelloWorld {
// 外部类中的静态变量score
private static int score = 84;
private static int b = 99;
// 外部类中的非静态变量a
private String a="非静态成员";
// 创建静态内部类
public static class SInner {
// 内部类中的变量score
int score = 91;
public void show() {
/*2、如果外部类的静态成员与内部类的成员名称相同,可通过“类名.静态成员”访问外部类的静态成员;
如果外部类的静态成员与内部类的成员名称不相同,则可通过“成员名”直接调用外部类的静态成员*/
System.out.println("访问外部类中的score:" +HelloWorld.score);
System.out.println("访问外部类中的b:" +b);
/* 1、静态内部类不能直接访问外部类的非静态成员,但可以通过 new 外部类().成员 的方式访问 */
System.out.println("访问外部类中的非静态成员a:" + new HelloWorld().a);
System.out.println("访问内部类中的score:" + score);
}
}
// 测试静态内部类
public static void main(String[] args) {
/*3、创建静态内部类的对象时,不需要外部类的对象,可以直接创建 内部类 对象名= new 内部类();*/
SInner si=new SInner();
// 调用show方法
si.show();
}
}
输出结果:
访问外部类中的score:84
访问外部类中的b:99
访问外部类中的非静态成员a:非静态成员
访问内部类中的score:91
方法内部类
☞内部类定义在外部类的方法中,方法内部类只在该方法的内部可见,即只在该方法内可以使用。
//外部类
public class HelloWorld {
private String name = "爱慕课";
// 外部类中的show方法
public void show() {
// 定义方法内部类【内部类定义在外部类的方法中,且只在该方法内可以使用】
class MInner {
int score = 83;
public int getScore() {
return score + 10;
}
}
// 创建方法内部类的对象
MInner mi=new MInner();
// 调用内部类的方法
int newScore=mi.getScore();
System.out.println("姓名:" + name + "\n加分后的成绩:" + newScore);
}
// 测试方法内部类
public static void main(String[] args) {
// 创建外部类的对象
HelloWorld mo=new HelloWorld();
// 调用外部类的方法
mo.show();
}
}
注意:由于方法内部类不能在外部类的方法以外的地方使用,因此方法内部类不能使用访问控制符和 static 修饰符。
继承
1.继承的概念:
继承是类与类的一种关系,是一种"is a"的关系;
注:Java中的继承是单继承(也就是一个类只有一个父类)
2.继承的好处:
子类拥有父类的所有属性和方法(属性和方法的修饰符不能是private,private的属性和方法是不能继承的)
修饰符 class 子类 extends 父类
例如
public class Dog extends Animal{
}
方法的重写:
如果子类对继承父类的方法不满意,是可以重写父类继承的方法的,调用方法时会优先调用子类的方法。
规则:
- 返回值类型
- 方法名
- 参数类型及个数
都要与父类继承的方法相同,才叫方法的重写。
继承的初始化顺序
1,初始化父类再初始化子类
2,先执行初始化对象中的属性,再执行构造方法中的初始化
父类
public class Animal {
public int age=10;
public String name;
public void eat(){
System.out.println("动物具有吃的能力");
}
public Animal(){
System.out.println("父类Animal执行构造方法");
age=20;
}
}
子类
public class Dog extends Animal{
public void eat(){
System.out.println("狗具有吃的能力");
}
public Dog(){
//super();隐试写了一个父类的构造方法。如果显示调用,必须在子类构造方法的第一行
System.out.println("子类Dog执行构造方法");
}
}
测试
public static void main(String[] args) {
Dog dog=new Dog();
dog.eat();
//输出:
父类Animal执行构造方法
子类Dog执行构造方法
狗具有吃的能力
Animal animal=new Animal();
System.out.println("Animal age:"+animal.age);
//输出:
父类Animal执行构造方法
Animal age:20(将原来的属性值10覆盖掉了,所以是先属性初始化)
}
final关键字放在访问修饰符之前。
选择题
如果用 final 修饰一个类的话,代表此类不可被继承。
不是静态类
能被实例化
能继承其他类
super关键字:
在对象的内部使用,可以代表父类对象
1、访问父类的属性
super.age
2、访问父类的方法
super.eat()
- 子类的构造过程中必须调用父类的构造方法
- 如果子类的构造方法中没有显示调用父类的构造方法,则系统默认调用父类无参的构造方法
- 如果显示的调用构造方法,必须在子类的构造方法的第一行
- 如果子类构造方法中既没有显示调用父类的构造方法,而父类又没有无参的构造方法(当有指定构造方法时,无论有参、无参的构造方法,将不会自动添加无参的构造方法),则编译出错。
Object 类
object类是所有类的父类,如果一个类没有使用extends关键字明确标示 继承另外一个类,那么这个类默认继承object类。
object类中的所有方法,适合所有子类。
1.toString()方法
在object类里面定义toString()方法的时候返回的对象的哈希code码(对象地址字符串)
可以通过重写toString()方法表示出对象的属性
2.equals()方法
比较的是对象的引用是否指向同一块内存地址。
Dog dog = new Dog()
一般情况下比较两个对象是比较他的值是否一致,所以要进行重写。
Dog 类中的age属性
@override
public boolean equals(Object obj) {
if (this == obj)//引用的值(地址)是否相同【相等表示指向同一个对象】
return true;
if (obj == null)//不会比较2个null对象的 一个为null一个不为null肯定不相等
return false;
if (getClass() != obj.getClass())//判断2个对象的类型
return false;
Dog other = (Dog) obj;
if (age != other.age)//属性值比较
return false;
return true;
}
类的对象:关心对象属性值的信息-----数据信息 。 类对象:类的代码信息(属性,变量名,方法)
没有重写 toString 方法的情况下直接用 print 打印对象,输出的是此对象的地址。
默认继承 Object 的 toString( ) 方法,输出对象地址。
多态
【继承是多态实现的基础】
对象的多种形态
1.引用多态
父类的引用可以指向本类的对象
父类的引用可以指向子类的对象【子类的引用不能指向父类的对象】
2.方法多态
创建本类对象时,调用的方法为本类方法
创建子类对象时,调用的方法为子类重写的方法或者继承的方法
【不能通过父类的引用调用子类独有的方法】
引用类型转换
1.向上(隐式/自动)小类型到大类型
2.向下(强制)大类型到小类型【有风险:数据溢出】
3.instanceof运算符,来解决引用对象的类型,避免类型转换的安全性问题【返回的是boolean值】
if(类 instanceof 类){
//例如 if(animal instanceof cat)//animal是父类,cat是子类
//如果动物对象中含有Cat元素,则可以转换---->true
}
抽象类
1.语法定义
抽象类前使用abstract关键字修饰,则该类为抽象类。
2.应用场景:
a.在某些情况下,某个父类只是知道其子类应该包含怎样的方法,但无法准确知道这些子类如何实现这些方法。
b.从多个具有相同特征的类中抽象出一个抽象类,以这个抽象类作为子类的模板,从而避免了子类设计的随意性。
3.作用:
限制规定子类必须实现某些方法,但不关注实现细节。
4.使用规则
定义抽象类
定义抽象方法【抽象方法没有方法体】,只有声明,不需要实现。
包含抽象方法的类是抽象类
抽象类中可以包含普通的方法,也可以没有抽象方法
抽象类不能直接创建,可以定义引用变量
包含抽象方法的类一定是抽象类,抽象类和抽象方法都需要添加关键字 abstract,且顺序为 abstract class
接口中方法不能有方法体,同时方法的访问修饰符不能是 private 和 protected
父类
public abstract class Telphone {
public abstract void call();
public abstract void message();
}
子类
public class Cellphone extends Telphone{
@Override
public void call() {
System.out.println("通过键盘来打电话");
}
@Override
public void message() {
System.out.println("通过键盘来发信息");
}
}
public class SmartPhone extends Telphone {
@Override
public void call() {
System.out.println("通过语音来打电话");
}
@Override
public void message() {
System.out.println("通过语音来发短信");
}
}
测试
Telphone tel=new Cellphone();
tel.call();
tel.message();
Telphone tel2=new SmartPhone();
tel2.call();
tel2.message();
//输出
通过键盘来打电话
通过键盘来发信息
通过语音来打电话
通过语音来发短信
接口
接口可以理解为一个特殊的类,由全局常量和公共的抽象方法组成
类是一种具体实现体,而接口定义了某一批类所需要遵守的规范,接口不关心这些类的内数据,也不关心这些类里方法的实现细节,它只规定这些类里必须提供某种方法。
定义类使用class,定义接口使用interface
【访问修饰符】【abstract如果没写,系统会默认加上】 interface 接口名【extends 父接口1,父接口2】{
0个到多个常量定义。。。。。
【属性是常量,即使定义时不添加 public static final修饰符,系统也会自动加上】
0个到多个抽象方法的定义。。。。
【接口中的方法只能是抽象方法,总是使用,即使定义时不添加public abstract 修饰符,系统也会自动加上】
}
接口是用来被继承、被实现的,访问修饰符一般建议使用public
不能使用private和protected
接口使用
一个类可以实现一个或多个接口,实现接口使用implements。Java中一个类只能继承一个父类,是不够灵活的,通过实现多个接口可以做补充
修饰符 class 类名 extends 父类 implements 接口1,接口2...
{
类体部分//如果继承了抽象类,需要实现继承的抽象方法;要实现接口中的抽象方法
}
如果要继承父类(extends 父类),继承父类必须在实现接口(implements 接口)之前
定义一个接口
//用户如果没有手动写上(abstract )的话,系统都会自动添加。一般abstract通常都不写
public interface IpayGame {//接口都是抽象类abstract
//属性都是static final
public void payGame();//方法也是abstract
}
public class Psp implements IpayGame {
@Override
public void payGame() {
System.out.println("Psp具有了玩游戏的功能");
}
}
public class SmartPhone extends Telphone implements IpayGame{
@Override
public void call() {
System.out.println("通过语音来打电话");
}
@Override
public void message() {
System.out.println("通过语音来发短信");
}
@Override
public void payGame() {
System.out.println("SmartPhone具有了玩游戏的功能");
}
}
测试
//接口引用===指向===一个实现的对象
//接口的引用(IpayGame ip1)可以指向一个实现接口的对象(new SmartPhone())
IpayGame ip1=new SmartPhone();
ip1.payGame();
IpayGame ip2=new Psp();
ip2.payGame();
输出:
SmartPhone具有了玩游戏的功能
Psp具有了玩游戏的功能
匿名内部类:没有名字的内部类。多用于关注实现而不关注实现类的名称
接口在使用过程中常与匿名内部类配合使用。
Interface i=new Interface(){
public void method(){
System.out.println("匿名内部类实现接口的方式");
};
测试
IpayGame ip3=new IpayGame(){
public void payGame() {
System.out.println("使用匿名内部类的方式实现接口 方式1");
}
};
ip3.payGame();
//直接new接口也可以调用方法
new IpayGame(){
public void payGame() {
System.out.println("使用匿名内部类的方式实现接口 方式2");
}
}.payGame();
//输出:
使用匿名内部类的方式实现接口 方式1
使用匿名内部类的方式实现接口 方式2