(十四)Java中的访问控制修饰符
- 访问控制修饰符
- public:公开的,任何类
- private:私有的,本类
- protected:受保护的,本类、派生类、同包类
- 默认的:什么也不写,本类、同包类,一般不建议
-
类的访问权限只能是public或默认的
-
类中成员的访问权限如上4种都可以
-
访问权限由大到小:public>protected>默认的>private
package ooday05; //访问控制修饰符的演示 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:最终的、不可改变的
- 修饰变量:变量不能被改变
- 修饰方法:方法不能被重写
- 修饰类:类不能被继承
- static:静态的
- 静态变量
- 由static修饰
- 属于类,存储在方法区中,只有一份
- 常常通过类名点来访问
- 何时用:所有对象所共享的数据(图片、音频、视频等)
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); //常常通过类名点来访问 } } //演示静态变量 class Loo{ int a; //实例变量 static int b; //静态变量 Loo(){ a++; b++; } void show(){ System.out.println("a="+a+",b="+b); } }
-
静态方法
-
由static修饰
-
属于类,存储在方法区中,只有一份
-
常常通过类名点来访问
-
静态方法中没有隐式this传递,所以不能直接访问实例成员
-
何时用:方法的操作与对象无关(不需要访问对象的数据)
public class StaticDemo { public static void main(String[] args) { int a = Noo.plus(5,6); //常常通过类名点来访问 } } //演示静态方法 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,意味着与对象有关,所以不能设计为静态方法 void show(){ System.out.println(a); } //在plus()中没有访问对象的属性,意味着与对象无关,所以可以设计为静态方法 static int plus(int num1,int num2){ int num = num1+num2; return num; } }
-
-
静态块
-
由static修饰
-
属于类,在类被加载时自动执行,一个类只被加载一次,所以静态块只执行一次
-
何时用:初始化/加载静态资源(图片、音频、视频等)
public class StaticDemo { public static void main(String[] args) { Poo o4 = new Poo(); Poo o5 = new Poo(); Poo o6 = new Poo(); } } //演示静态块 class Poo{ static{ System.out.println("静态块"); } Poo(){ System.out.println("构造方法"); } }
-
- 静态变量
- 成员变量分两种
-
实例变量:没有static修饰,属于对象的,存储在堆中,有几个对象就有几份,通过引用打点来访问
-
静态变量:由static修饰,属于类的,存储在方法区中,只有一份,通过类名打点来访问
-
-
内存管理:由JVM管理
-
堆:new出来的对象(包括实例变量)
-
栈:局部变量(包括方法的参数)
-
方法区:.class字节码文件(包括静态变量、所有方法)
-
(十五)抽象方法和抽象类
- static final常量
- 必须声明同时初始化
- 常常由类名来访问,不能被改变
- 建议:常量名所有字母都大写,多个单词用_分隔
- 编译器在编译时会将常量直接替换为具体的值,效率高
- 何时用:数据永远不变,并且经常使用
public class StaticFinalDemo { public static void main(String[] args) { System.out.println(Aoo.PI); //常常由类名点来访问 //Aoo.PI = 3.1415926; //编译错误,常量不能被改变 //1)加载Boo.class到方法区中 //2)静态变量num一并存储在方法区中 //3)到方法区中获取num并输出 System.out.println(Boo.num); //编译器在编译时会将常量直接替换为具体的值,效率高 //相当于: System.out.println(5); System.out.println(Boo.COUNT); } } class Boo{ public static int num = 5; //静态变量 public static final int COUNT = 5; //常量 } class Aoo{ public static final double PI = 3.14159; //public static final int NUM; //编译错误,常量必须声明同时初始化 }
- 抽象方法
- 由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); //Mama.this指代当前对象的外部类对象 //System.out.println(this.name); //编译错误,this指代当前Baby对象 } } }
- 匿名内部类
- 若想创建一个类(派生类)的对象,并且对象只被创建一次,可以做成匿名内部类
- 匿名内部类中不能修饰外面局部变量的值,因为在此处该变量会默认为final的
public class AnonInnerClassDemo { public static void main(String[] args) { //new Aoo(); //创建Aoo对象 //new Aoo(){}; //创建Aoo的派生类的对象 //1)创建了Aoo的一个派生类,但是没有名字 //2)为该派生类创建了一个对象,名为o1 //3)大括号中的为派生类的类体 Aoo o1 = new Aoo(){ //向上造型 }; //1)创建了Aoo的一个派生类,但是没有名字 //2)为该派生类创建了一个对象,名为o2 //3)大括号中的为派生类的类体 Aoo o2 = new Aoo(){ }; int num = 5; num = 55; //1)创建了Boo的一个派生类,但是没有名字 //2)为该派生类创建了一个对象,名为o3 //3)大括号中的为派生类的类体 Boo o3 = new Boo(){ void show(){ //重写Boo类的抽象方法 System.out.println("showshow"); //num = 66; //编译错误,在此处会默认num为final的 } }; o3.show(); } } abstract class Boo{ abstract void show(); } abstract class Aoo{ }
- 小小面试题:
- 问:内部类有独立的.class吗?
- 答:有!
(十七)Java中的接口
- 接口
- 是一种引用数据类型,由interface定义
- 只能包含常量和抽象方法
- 接口不能被实例化(new对象)
- 接口是需要被实现/继承的,实现类/派生类:必须重写所有抽象方法
- 一个类可以实现多个接口,用逗号分隔。若又继承又实现时,应先继承后实现
- 接口可以继承接口
//接口的演示 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(){} public void say(){} } //演示接口继承接口 interface Inter4{ void show(); } interface Inter5 extends Inter4{ void test(); } class Doo implements Inter5{ public void test(){} public void show(){} }
- 接口与类相似点
- 一个接口可以有多个方法
- 接口文件保存在 .java 结尾的文件中,文件名使用接口名
- 接口的字节码文件保存在 .class 结尾的文件中
- 接口相应的字节码文件必须在与包名称相匹配的目录结构中
- 接口与类的区别
- 接口不能用于实例化对象
- 接口没有构造方法
- 接口中所有的方法必须是抽象方法,Java 8 之后 接口中可以使用 default 关键字修饰的非抽象方法
- 接口不能包含成员变量,除了 static 和 final 变量
- 接口不是被类继承了,而是要被类实现
- 接口支持多继承
- 接口特性
- 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)
- 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误
- 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法
- 接口和抽象类的区别
- 接口是行为的抽象,是一种行为的规范,接口是like a 的关系;抽象是对类的抽象,是一种模板设计,抽象类是is a 的关系
- 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行
- 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法
- 一个类只能继承一个抽象类,而一个类却可以实现多个接口。所以抽象体现出了继承关系,继承只能单继承。接口提现出来了实现的关系,实现可以多实现。接口强调特定功能的实现,而抽象类强调所属关系
- 接口成员变量默认为public static final,必须赋初值,不能被修改;其所有的成员方法都是public abstract的。抽象类中成员变量默认default,可在子类中被重新定义,也可被重新赋值;抽象方法被abstract修饰,不能被private、static、synchronized和native等修饰,必须以分号结尾,不带花括号
(十八)Java中的多态
- 多态的意义
- 同一个对象被造型为不同的类型时,有不同的功能
- 对象多态:我、你、水-------------------所有对象都是多态的
- 同一类型的引用指向不同的对象时,有不同的实现
- 行为多态:cut(),move(),getImage()......---------所有抽象方法都是多态的
- 同一个对象被造型为不同的类型时,有不同的功能
- 向上造型
- 超类型的引用指向派生类的对象
- 能点出来什么,看引用的类型
- 能造型成为的类型有:超类+所实现的接口
- 强转类型转换,成功的条件只有如下两种:
- 引用所指向的对象,就是该类型
- 引用所指向的对象,实现了该接口或继承了该类
- 强转时若不满足如上条件,则发生ClassCastException类型转换异常,建议:在强转之前先通过instanceof来判断引用指向的对象是否是该类型
public class MultiTypeDemo { public static void main(String[] args) { Aoo o = new Boo(); //向上造型 Boo o1 = (Boo)o; //引用o指向的对象,就是Boo Inter o2 = (Inter)o; //引用o指向的对象,实现了Inter接口 //Coo o3 = (Coo)o; //运行时发生ClassCastException类型转换异常 if(o instanceof Coo){ Coo o4 = (Coo)o; }else{ System.out.println("o不是Coo类型"); } } } interface Inter{ } class Aoo{ } class Boo extends Aoo implements Inter{ } class Coo extends Aoo{ }
(十九) Java中的内存管理(由JVM管理)
- 堆
- 存储new出来的对象(包括实例变量、数组元素)
- 垃圾:没有任何引用所指向的对象
- 垃圾回收器(GC)不定时到堆中回收垃圾,回收过程是透明的(看不到的),不一定一发现垃圾就立刻回收,通过调用System.gc()建议JVM尽快调度GC来回收
- 实例变量的生命周期
- 创建对象时存储在堆中,对象被回收时一并被回收
- 内存泄漏:不再使用的对象还没有被及时的回收
- 建议:不再使用的对象应及时将引用设置为null
- 栈
- 存储正在调用的方法中的局部变量(包括方法的参数)
- 调用方法时会在栈中为该方法分配一块对应的栈帧,栈帧中存储局部变量(包括方法参数),方法调用结束时,栈帧被清除,局部变量一并被清除
- 局部变量的生命周期
- 调用方法时存储在栈中,方法调用结束时与栈帧一并被清除
- 方法区
- 存储.class字节码文件(包括静态变量、所有方法)
- 方法只有一份,通过this来区分具体的访问对象
(二十)面向对象三大特征
面向对象的三个基本特征是:封装、继承和多态
- 封装:隐藏部分对象的属性和实现细节,对数据的访问只能通过外公开的接口。通过这种方式,对象对内部数据提供了不同级别的保护,以防止程序中无关的部分意外的改变或错误的使用了对象的私有部分
- 类:封装的是对象的属性和行为
- 方法:封装一段特定的业务逻辑功能
- 访问控制修饰符:封装的是具体的访问权限
- 继承:让某个类型的对象获得另一个类型的对象的属性的方法。继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为
- 作用:代码复用
- 超类:所有派生类共有的属性和行为
- 接口:部分派生类共有的属性和行为
- 派生类:派生类特有的属性和行为
- 单一继承、多接口实现,具有传递性
- 多态:对于同一个行为,不同的子类对象具有不同的表现形式。多态存在的3个条件:A.继承 B.重写 C.父类引用指向子类对象
- 所有对象都是多态的(通过向上造型来体现)
- 所有抽象方法都是多态的(通过方法的重写来体现)
- 向上造型、方法重写、强制类型转换、instanceof判断
本文完!
写在结尾:
2022 年 9 月 22 日 一个java菜鸟,发布于北京海淀。
好记性不如烂笔头,持续学习,坚持输出~今天是持续写作的第3天。可以点赞、评论、收藏啦。