JAVA-面向对象OO
文章目录
面向对象 (Object-Oriented)
面向对象编程思想
-
面向对象:
编程使用的一种编程思路,编程思想。更强调解决问题的本体、谁来解决这个问题、数据、对象、谁拥有数据和操作数据的权力。编程思路:自底向上。不断拼装。
-
面向过程:
面向对象的基础,它也是一种编程思路。更强调解决问题的基本步骤、问题的解决方式、方法。编程思路:自顶向下,不断分解。
-
面向对象和面向过程的区别:
实际解决问题的方式不同。
关系:都是编程思想,面向过程是面向对象的基础,面向对象基于面向过程的。 -
面向对象的好处:
- 更符合人类的思想习惯:凡事喜欢找对象、找工具、找人,而不是找方法。
- 复杂的问题简单化。
- 由执行者变成了指挥者。
-
面向对象的特征:
- 封装
- 继承
- 多态
- (抽象)
类和对象
-
类和对象
-
类 class:
类型,对具体事物的一个抽象认识,是抽象出来的结果,其实就是一个概念。
其实就是一个概念对一类事物的统称和概括,是一种归纳,类就是抽象出来的概念而已。
-
对象 object:
事物的具体体现。
对象是具体的个体,是一类事物中具体的个例或者实例。
在java语言编程中,对象又可以称为实例。实例就是对象,我们可以把对象和实例等价。
-
-
类的说明
-
类就是一组相关属性和行为的集合。
-
属性:对于事物特征的描述。
java语言中,就是一个变量,和以前定义的方式一样,只不过位置发生了改变。
以前定义在方法中,现在定义在类中方法外。
-
行为:对于事物功能的描述。
java语言中,就是一个成员方法,和以前定义的方式一样,只不过修饰符发生了改变。
以前修饰符是public static,现在将static去掉。
-
重点:
类基本的构成就是属性和行为,其他的任何内容都是对属性或行为进行操作。
-
说明:
自定义类编译之后,也会生成一个.class的字节码文件。
在一个源文件中,可以定义多个类,一旦编译该文件,就会将所有的类型进行编译。
public后面的类名要和类文件名一致。
-
对象的创建和使用
-
对象的创建格式:
类名 对象名 = new 类名();
-
格式内容解释:
- 类名:要创建的对象所属的类型
- 对象名:一个合法的标识符即可,用于表示创建的对象
- =:将对象的地址,赋值给对象名
- new:在堆内存中开辟一段空间,用于存储类型的数据
- 类名:和前面声明引用的类名一样
- ():表示调用一个方法,类的构造方法
-
访问属性:
对象名.属性名(访问)
对象名.属性名 = 属性值;(修改)
-
访问方法:
对象名.方法();
public class Demo_01 {
public static void main(String[] args) {
Person p = new Person(); // 创造对象(空参构造)
p.name = "Danny"; // 通过对象访问属性
p.age = 22;
System.out.println("姓名:" + p.name + ",年龄:" + p.age);
p.eat(); // 通过对象访问方法
p.sleep();
p.work();
}
}
class Person { // 创建类
String name; // 创建属性
int age;
void eat() { // 创建行为
System.out.println("吃饭");
}
void sleep() {
System.out.println("睡觉");
}
void work() {
System.out.println("工作");
}
}
Output:
姓名:Danny,年龄:22
吃饭
睡觉
工作
创建对象的内存理解
- 创建对象的过程
- 将要创建的对象所属的类型的字节码文件,加载到方法区中
- 在栈内存中声明了一个该类型的引用,将来用于存储在堆内存为成员变量分配的内存的地址
- 在堆内存中开辟内存空间,给各个成员变量进行内存的分配
- 给对象中的成员变量进行默认初始化赋值
- 将对象的地址,赋值给栈内存中声明的引用
- 内存图的说明
- 第一次使用某个类型的时候,会将该类型的字节码文件加载到方法区中,第二次使用的时候,不会重复加载了
- 在创建对象的时候,默认隐含了该对象对应的字节码文件的地址,将来可以通过对象找到创建自己的字节码对象的。
- 那个对象调用方法,方法的栈帧中,就默认隐含了该对象的地址。
- 每次使用new关键字,那么一定会在堆内存中,开辟一段新的内存空间
成员变量和局部变量
-
成员变量:定义在类中的成员位置的变量
局部变量:定义在方法中的变量
-
不同点:
-
代码层面:定义位置不同
-
局部变量:定义在方法中,或在方法声明上(方法的形式参数)
-
成员变量:定义在类中方法外
-
-
内存层面:空间不同
-
局部变量:属于方法,和方法的位置相同,在栈内存的方法的栈帧中
-
成员变量:属于对象,和对象位置相同,在堆内存的对象中
-
-
内存层面:时间不同,生命周期不同
- 局部变量:随着方法的调用而存在,随着方法调用的结束而消失
- 成员变量:随着对象的创建而存在,随着对象的消失而消失(栈内存中没有任何引用指向该对象时,该对象就变成了垃圾,片刻之后就会被垃圾回收线程回收掉)
-
初始化状态不同
-
局部变量:没有默认的初始化值,必须先手动赋值,才能使用
-
成员变量:有默认的初始化值
引用数据类型:null;基本数据类型:整数:0,小数:0.0,布尔:false,字符:’\u0000’
-
-
匿名对象的理解和使用
-
匿名对象:没有名字的对象
-
本质:就是在栈内存中没有任何的引用变量保存对象的地址
-
定义格式:new 类名();
-
匿名对象的使用场景:
- 如果某个对象在创建之后,其方法只调用一次,那么就可以使用匿名对象来调用。这种写法会节省一些内存空间。
- 可以作为某个方法的实际参数。这种调用形式,在主方法中可以将该对象看作匿名对象,但是在被调用的方法中,这个对象是引用的对象报数匿名对象。
- 可以作为某个方法的返回值。这种调用形式,在被调用方法中,这个对象是匿名对象,但是在调用着,这个对象可能不是匿名对象。
-
注意事项:
匿名对象可以给成员变量赋值,但是这样做没有任何意义。因为匿名对象一旦被赋值,匿名对象就变成了垃圾,很快就会被回收掉。即使没有被回收,我们也无法使用这个匿名对象。
封装 (Encapsulation)
封装的概述
-
封装:隐藏事物的属性和实现细节,对外提供公共的访问方式
-
封装的好处:
-
隐藏了事物的实现细节
-
提高了代码的复用性
-
提高了代码的安全性
-
-
封装的原则:
-
隐藏事物的属性
-
隐藏事物的实现细节
-
对外提供公共的访问方式
-
private关键字
- private单词:私有的,私密的
- 可以修饰的内容:
- 修饰成员变量
- 修饰成员方法
- 修饰构造方法
- 修饰内部类
- 修饰后的结果:被private修饰的成员,只能在本类中被访问。
- private关键字的注意事项:private只是封装的一种体现形式,封装还可以用其他的修饰符来完成。
Getter和Setter
(快捷键:IDEA: Alt+Ins、Eclipse: Shift+Alt+s)
- 当属性被私有之后,外界无法直接访问,所以需要提供公共的访问方式,让外界可以间接的访问属性。对于当前类,就可以控制外界访问属性的方法。
- 一般提供get方法,获取成员变量的值、提供set方法设置成员变量的值。
变量访问原则和this关键字
-
变量访问的就近原则:
变量的定义,声明:带数据类型的变量
变量的使用,访问:不带数据类型的变量
就近原则:当在访问某个变量名称的时候,会先寻找最近的该变量名称的定义,如果找到了,就使用该变量,如果没找到,才到更远的位置寻找该变量名称的定义。
当局部变量和成员变量同名的时候,一定是先使用局部位置定义的变量,如果没有,才会使用成员位置定义的变量。
-
this关键字:
表示当前类型当前对象的引用
哪个来调用this所在的方法,this就代表哪个对象
作用:用来区分局部变量和成员变量同名的情况。使用this.属性名称的一定是成员变量,没有使用this.的变量,会根据就近原则来确定使用哪个变量。
构造方法概述
-
构造方法:构造函数,构造器,Constructor
-
作用:用于给对象中的成员变量赋值。在创建对象的同时,会自动调用构造方法,等对象创建完成的时候,对象中的成员变量就已经有指定的值了。
-
构造方法的定义格式:(快捷键:IDEA: Alt+Ins、Eclipse: Shift+Alt+s)
修饰符 方法名称 (参数列表){ 方法体
}
-
构造方法格式的说明:
- 构造方法的方法名称,必须和类名一模一样,连大小写都一样
- 构造方法没有返回值类型,连void也没有
- 构造方法没有return语句,如果一定需要return语句,就写一个return;
-
构造方法其他说明:
- 构造方法不需要手动调用,由JVM虚拟机在创建对象的时候自动调用
- 对象本身不能调用构造方法
- 构造方法只能调用一次
构造方法的注意事项
-
构造方法可以是有参数的,也可以是没有参数的
-
如果是没有参数的构造方法,外界无需传入任何的参数值,只能给成员变量赋固定值或者不赋值。
-
如果是有参数的构造方法,外界在调用构造方法的时候,需要传入实际的参数值,用于赋值给成员变量。
-
-
如果在类中没有定义任何的构造方法,那么系统会自动提供一个空参构造(空实现)
-
如果在类中手动定义了一个构造方法(无论是空参还是有参),系统都不再会提供任何构造方法。
-
构造方法的重载:
- 在同一个类中,方法名相同,参数列表不同,与返回值类型无关
- 构造方法都在同一个类中,构造方法的方法名称都和类名一致,【参数列表不同】,返回值类型没有
- 一般在类中,既需要空参构造,也需要有参构造,都需要手动定义出来
-
注意:在使用有参构造的时候,一定要将空参构造也要写上。
创建对象内存理解(三个初始化过程)
-
创建对象的过程中,有三个初始化的步骤及三者顺序:
顺序:默认初始化、显式初始化、构造方法初始化
-
创建对象的过程
- 将创建的对象所属类型加载到方法区
- 在栈内存中创建对象的引用,将来用于存储对象的地址
- 在堆内存中开辟内存空间,给成员变量分配内存的过程
- 给成员变量进行默认初始化的赋值
- 给成员变量进行显示初始化的赋值
- 给成员变量进行构造初始化赋值
- 将对象的在堆内存中的地址,赋值给栈内存中的引用
public class Demo_02 {
public static void main(String[] args) {
Student st = new Student(); // 建造对象
st.setName("Danny"); // 外界可以通过对象调用Setter方法来间接的设置私有成员变量
System.out.println("学生姓名:" + st.getName());// 通过对象调用Getter方法来访问私有成员变量
Student st2 = new Student("Angel"); //有参构造创建对象
System.out.println("学生姓名:" + st2.getName());
}
}
class Student {
private String name; //封装成员变量
public String getName() { //Getter
return name;
}
public void setName(String name) { //Setter
this.name = name; // 局部变量与成员变量重名时用this来区分
}
public Student() { // 空参构造
super(); // 如果没有父类则默认继承java.lang.Object类
}
public Student(String name) { // 有参构造
super();
this.name = name;
}
}
Output:
学生姓名:Danny
学生姓名:Angel
静态(static)
静态的概述
- 没有静态:如果某个类型的所有对象,都具有相同的属性值,那么这个属性值就没有必要在所有对象中,都存储一份。还有坏处:浪费内存空间;维护难度大,一旦需要修改,就得修改所有的对象。
- 有静态的状态:如果某个类型的所有对象,都具有一个相同的属性值,那么就在这个属性的定义上,加一个static静态关键字。让该变量存储在方法区字节码的静态区中,避免了所有对象都存储相同数据的问题,节省了内存空间,将来维护容易(只需要修改一次)
静态变量的特点
-
静态的解释:static 关键字
静态,静止的。静态变量是属于类的,被所有对象所共享
-
加载时机:
随着类的加载而加载
静态变量随着类的加载,加载到方法区,就直接在方法区中进行内存的开辟和分配的过程
-
静态变量是优先于对象而存在的。
普通的成员变量一定是只能创建完对象之后才能通过对象名,属性名进行访问,但是静态变量可以在创建对象之前就进行访问。
-
静态变量被所有该类对象所共享
-
代码方面:
可以使用类名直接调用,不需要使用对象名称。在不创建对象的前提下,仍然可以使用这个静态变量。建议使用类名访问。
静态访问的注意事项
-
静态方法:在方法声明上,加上了static关键字的方法,就是静态方法
-
静态方法不能访问非静态的变量
原因:静态方法也是属于类的,可以在不创建对象的前提下,就进行调用,但是非静态的变量一定是在创建完对象之后才会进行内存的分配才能进行使用,因此静态方法如果能访问非静态变量,那么就相当于该变量还没有进行内存的分配,就对该变量进行了使用,显然是错误的。
-
静态方法不能访问非静态方法
原因:静态方法可以在没有创建对象的时候,就进行调用,非静态方法只能在创建完对象之后才能调用,非静态方法可以访问非静态变量的,如果 静态方法可以访问非静态方法,就相当于间接的访问了非静态变量,这显然也是矛盾的,不正确的,因此静态方法,也是无法访问非静态方法的
-
静态方法中不能存在this关键字的
原因:this关键字表示本类的当前对象,静态方法可以在创建对象之前进行调用,如果静态方法可以使用this关键字,就相当于在创建对象之前 就使用了对象,这显然是不合理的,因此在静态方法中不能使用this关键字。
-
总结:
静态不能访问非静态
静态变量和非静态变量的区别
-
概念上所属的不同
静态变量属于类
非静态变量属于对象 -
内存空间不同,存储的位置不同
非静态变量属于对象,所以在堆内存中
静态变量属于类,存储在方法区中 -
内存上时间的不同,生命周期不同
非静态变量属于对象,所以生命周期和对象相同,随着对象的创建而存在,随着对象的消失而消失
静态变量属于类,所有生命周期和类相同,随着类的加载而存在(类的加载:使用的类名的时候,反射,加载子类等等),随着类的消失而消失(内存管理)
-
访问方式的不同:
非静态变量和非静态方法,在访问的时候,只能通过对象名.属性名,或者对象名.非静态方法名()进行访问
静态变量和静态方法,在访问的时候,可以通过类名.静态变量名,或者类名.静态方法名()进行访问。
工具类的编写
工具类的概述
在一个类中,主要的目的就是为了 快速的解决一些问题,而定义的一些方法,因此在工具类中没有必须维护成员变量。方法都是一些静态的方法。更方便的使用,解决问题。对于工具类就不需要创建对象。浪费内存。希望控制不让工具类创建对象。
帮助文档的制作
- 工具类准备好之后,编译之后生成.class字节码文件,外界不能看懂,所以需要编写工具类的程序员,要准备一份工具类的帮助文档。
- 文档注释:用于给代码生成帮助文档的注释
格式:
/**
文档注释的内容
*/ - 助文档的注解:将来可以被文档生成工具进行解析的一种格式。可以解析其中的数据
作者:@author fang
版本:@version 12
从那个版本开始:@since 1.0
参数:@param 参数名称 参数名称的解释
返回值:@return 返回内容的解释 - 生成帮助文档
使用jdk中bin目录下提供的javadoc这个工具,就可以生成帮助文档
javadoc -d arrayTool -author -version 源代码文件
帮助文档的使用
- 虽然以后不去制作帮助文档,但是对于已经存在的工具类或者jdk中提供的一些类型,要频繁的使用,就要学会使用jdk提供的帮助文档。
- 在线帮助文档
- 离线的帮助文档
- 打开文档
- 点击显示
- 点击索引
- 输入要搜索的类型
- 阅读对该类型的说明及示例代码
- 查看类型的构造方法,判断是否可以创建对象
- 查看每个方法的介绍,判断是否是自己需要的方法
- 查看该方法的参数列表的解释,和返回值
- 查看该方法是否是静态,是静态就可以直接通过类名.方法名进行调用。
继承(extend)
继承的概述
- 让类和类产生关系,父子类的关系
- extends,关键字,扩展,增加
- 父类和子类
父类:被继承的类,超类,基类
子类:用于继承的类,派生类。
继承的注意事项
-
私有的成员变量不能直接被子类进行访问的
父类中的私有成员,在子类中不能直接的使用
其实在子类对象中,仍然包含了父类中定义的私有的成员变量
只不过在子类中,不能直接访问父类的私有成员变量的
-
父类中的构造方法,不能继承
原因:父类的构造方法需要和父类的类名保持一致,子类的构造方法名称需要和子类的类名保持一致
父类和子类类名不一样,构造方法无法进行继承的。
父类的构造方法用于给父类中的成员变量进行赋值,子类的构造方法用于给子类的成员变量赋值。
使用父类的构造方法肯定是无法对子类中的成员变量进行赋值,因此不能继承父类的构造方法。
-
虽然父类的构造方法无法被继承,但是父类的构造方法可以由子类进行调用,完成对父类成员变量的赋值
-
继承的设计:不要为了部分的功能而定义继承
继承关系:是不是的关系。
子类是父类的一种
高内聚,低耦合
java中继承的特点
-
java支持单继承,不支持多继承,java支持多层继承
-
单继承:一个子类只能继承一个父类(一个孩子只能有一个亲爹)
-
多继承:一个子类可以有多个父类(一个孩子有多个亲爹),java中支持
-
多层继承:A类可以继承B类,C类可以继承A类,C类中就拥有了A类和B类中所有的属性和方法
越顶层的类,定义的功能都是共性的功能。功能和属性就越少,越是底层的类,定义的功能和属性就越多,就更多加强大。
学习的一个体系的过程中,都是从顶层的父类进行学习,然后再去学习子类中特有的方法即可。
-
-
原因:
如果支持多继承,那么可能一个子类就能继承多个父类,多个父类中就有可能出现相同的方法声明,具有不同的方法实现,此时对于子类而言,就不知道应该继承那个父类的方法了(安全隐患)
继承中成员变量的关系
-
在子父类中定义了不同名称的变量,在子类中,既可以访问子类的成员变量,也可以访问父类的成员变量
-
在子父类中出现了同名的成员变量,在子类中,根据就近原则进行访问
在子类的方法中,如果访问了某个变量名称。优先在当前方法中,寻找该变量的定义,如果找到了就使用方法中的局部变量,如果没有找到,就到子类的成员位置寻找该变量的定义,如果找到了就使用子类成员位置的变量,如果还没找到,就想父类中找,找到就使用父类中声明的该变量,如果还没找到就接着向父类中找,如果一直到Object类,如果还没有找到,编译报错。
this和super的使用
-
含义:
this:关键字表示是本类当前对象的引用
那个对象调用this所在的方法,this就表示那个对象
super:关键字表示本类当前对象父类的引用
那个对象调用super所在的方法,那么super表示的就是父类中的数据 -
this和super都可以访问成员变量的
this既可以访问父类中的成员变量,也可以访问子类中的成员变量
this.变量名
super 只能访问父类中定义的成员变量
super.变量名 -
this和super都可以访问成员方法
this既能调用父类中的方法,也能调用子类中的方法
this.方法名
super只能调用父类中的定义的方法
super.方法名 -
this和super都可以访问构造方法:this语句和super语句
this(实际参数);访问本类中的构造方法
super(实际参数):访问父类中的构造方法
继承中构造方法的关系
-
构造方法的作用:完成对成员变量的赋值
-
在初始化子类数据之前,必须先完成对父类数据的初始化(因为初始化子类数据的时候,可能会使用到父类中的数据,因此要先将父类中的数据准备好,其次就是子类是能够使用父类中的数据的,子类能够使用父类数据的前提是要将父类中的数据进行初始化的过程。)
-
实例化的本质,调用构造函数分配实例标识。申请内存存放实例中各个成员变量的初始值。
-
如果在子类的构造方法中,没有显示的调用父类中任何的构造方法,可以就可以简单的理解为该构造方法的第一句,默认加上了super();
-
如果在子类的构造方法中,手动了增加了一访问父类的构造方法,那么系统就不再提供任何的构造方法的访问了
-
构造方法不能递归调用
-
总结:
-
子类的构造方法,一定要先访问父类的构造方法
-
特点:
-
this语句和super语句必须放在构造方法的第一行的。
构造方法的作用就是完成对成员变量的初始化过程,只有完成对成员变量初始化之后,我们才能对变量进行使用super语句放在第一行保证父类中的数据先进行初始化
this语句放在第一行保证子类中的数据先进行初始化 -
this语句和super语句是不能共存的
-
在代码层面:this语句和super语句只能放在构造方法中的第一行,放在其他的方法中会报错。
-
-
继承中成员方法的关系
- 在子父类中出现了不同名的方法
子类对象既可以调用父类中的方法,也可以调用子类中的方法
-
在子父类中出现了同名的方法
在子父类中,出现了一模一样方法声明,但是两个方法的实现内容是不一样的。
作用:子类和父类产生差异的一种方法,在子类中,如果不想修改父类中的方法的声明,但是还想修改此方法的实现内容的时 候,这时候就可以使用方法的重写。
-
方法的重写和方法的重载
重载:在同一个类,或者父子类中,方法名相同,参数列表不同,与返回值类型无关。
重写:在子父类中,方法名相同,参数列表相同,和返回值类型有关(相同还可以是父子类的关系)
别名:override ,覆盖 -
方法重写的检查
@override
让编译器帮助我们检查当前的方法,是否是对父类中的方法进行重写 -
说明:
方法本身还是属于父类,只是在子类中重新将这个方法进行了实现。
重写的注意事项
-
私有的方法不能被重写
父类中的私有的方法,子类根本就看不到,继承不下来,无法完成对这个方法进行重写。
子类自己定义了一个和父类私有方法同名的方法,不算重写,只算是在子类中新定义一个方法 -
方法在重写的时候,权限不能越来越小
权限只能是约来越大,不能将权限给写没了
-
父类带final和static关键字的方法可以被子类继承,虽然不能重写但是可以再次声明。
-
子父类在同一个包中:子类可以重写所有父类的方法,除了private和final
子父类在不同的包中:子类可以重写父类声明为public和protected的非final方法
-
父类的构造方法不能被重写
面向对象的综合内容
代码块
代码块的概述
- 使用一对大括号括起来的代码,放在不同的位置,具有不同的执行时机,有不同的名称,和不同的作用
- 分类:
- 局部代码块
- 构造代码块
- 静态代码块
- 同步代码块(多线程)
局部代码块
-
格式:使用大括号括起来的一段代码
-
位置:类中方法中
-
作用:
限定变量的生命周期
在局部代码块中,声明的变量,只能在局部代码块范围内进行使用,一旦出了局部代码块的大括号,变量就不能继续使用了。 某个变量一旦不能使用了,就会被回收,节省内存空间
-
注意:
-
如果是在局部代码块中声明了变量,会减少变量的声明周期,出了局部代码块就无法继续使用局部代码块中声明的变量。
-
如果在局部代码块中修改了局部代码块外声明的变量,局部代码块结束之后,并不会消除局部代码块对这个变量的修改。
-
构造代码块
-
格式:使用大括号括起来的一段代码
-
位置:类中方法外
-
作用:在构造方法之前,完成对成员变量的赋值
可以将构造方法中都要执行的内容,提取到构造代码块中
-
执行时机
- 在创建对象的时候,由JVM默认调用一次
- 在构造方法执行之前,执行
- 任意一个构造方法在执行之前,都会执行一次构造代码块中的内容
- 如果每个构造方法都会执行的内容,提取到构造代码块中
静态代码块
-
格式:
static{
静态代码块中的内容;
} -
位置:类中方法外
-
作用:
用于给静态变量初始化赋值
用于在类的加载的时候,进行执行一次的内容,eg:驱动加载 -
执行特点:
- 随着类的加载而执行
- 类只加载一次,所以静态代码块只执行一次
- 执行的时机的最早,早于所有的对象相关内容
final关键字
-
单词含义:最终的,最后的,表示不能再改变的
-
final关键字:可以修饰类,方法,变量
-
修饰类:
表示一个最终类,表示不能有子类(不能被其他类继承)
不影响当前类的方法被调用
对于final修饰的类,没有子类就不存在继承,也就不存在重写的问题
可以继承其他的类,被final修饰的类,没有子类但是可以有父类 -
修饰方法
表示一个最终的方法【该方法不能被重写的】
但是被final修饰的方法,可以被子类继承 -
修饰变量
表示一个最终的变量,该变量变成了常量,就只能赋值一次。使用一个变量的符号去表示常量的值,因此这也被称为符号常量。
定义常量的好处:见名知意,容易理解;可维护性高
内部类
内部类的概述
-
定义在内部的类,就是内部类,可以定义在类中,也可以定义在类中方法中
-
根据定义位置的不同;
成员内部类
局部内部类 -
成员内部类:
普通的成员内部类
私有的成员内部类
静态的成员内部类 -
局部内部类
-
根据表示方式的不同,可以分为:
有名字的内部类
【匿名内部类】
普通的成员内部类
-
定义在成员位置上的类,就是成员内部类
-
格式:
class 外部类类名{
class 内部类类名{
}
} -
说明:
-
内部类可以直接访问外部类的所有成员,包括私有成员
-
外部类访问内部类的成员,必须先创建内部类的对象
-
在外部类以外,想直接创建内部类的对象,格式:
外部类名.内部类名 内部类对象名 = new 外部类名().new 内部类名();
-
私有的成员内部类
- 也是成员内部类,就是在成员内部类前面加上一个private关键字
- 访问方式说明:
- 在外部类以外,不能直接的访问外部类中的私有的成员内部类
- 要想对私有的成员内部类进行访问,那么定义一个访问私有成员内部类的公有的方法,让外界可以调用公有方法,间接的访问私有的成员内部类中的数据。
静态的成员的内部类
- 也是成员内部类,在成员内部类的前面加上了一个static关键字
- 访问特点:
- 静态成员内部类是外部类的静态成员。可以通过外部类类名.内部类类名的方式直接访问,而不需要创建外部类对象。
- 静态内部类中的非静态成员,需要将所在的内部类对象创建出来之后,才能被使用
- 一个类是否需要创建对象,不取决于本类是否是静态,而是取决于本类中的成员是否是静态的
- 静态成员内部类可以在其他类中创建对象的。格式
外部类名.内部类名 内部类对象名 = new 外部类类名.内部类类名();
局部内部类
-
局部内部类:定义在方法中的内部类
-
位置:类中方法中
-
访问说明:
方法中的局部变量,外界都没有办法访问到
在方法中定义的内部类,外界也是无法访问到的 -
解决方式:
在方法内部,就创建局部内部类的对象,调用对象的方法
外界调用局部方法,间接的创建对象,完成对局部内部类中内容的访问。
包(package)
-
包:用于分类存放类文件(.class)文件的文件夹
-
作用
- 分类存放类文件
- 在不同的包中,可以起相同的类名。
-
命名:
域名倒着写:com.offcn.demos(全球唯一)可以避免包名重复
-
包的声明:
使用package关键字,声明了当前类所属的包
-
效果:
- 编译这个类,就会将该类的字节码文件放入知道package后指定的文件夹路径中
- 当前的类的名称也会发生变量,会将类名和包名进行绑定,形成新的类名[包名+类名] 全类名
-
运行:
- 包文件夹以外,无法直接访问到这个类了
- 进入到包文件夹中,无论是写全类名还是类名都是无法访问的
- 【解决】在包文件夹所在的目录下,使用该类的全类名,才能成功的进行访问
-
访问:
在其他类访问带包的类的时候,都要使用那个类的全类名:包名+类名
-
简化:
- 每次使用其他包中的类的时候,都需要写很长一串的全类名,非常麻烦,需要简化
- 使用import关键字完成对格式的简化。在类的声明上面写一个import语句,将这个全类名导入,在该类中,
-
注意:
- 即使在一个包中,有好多类要使用,不建议使用 import *
- 如果在一个文件中农,需要使用两个包中的同名类,此时要注意,要么全都使用全类名,要么其中一个使用全类名,一个使用导包语句。(不建议用import否则无法区分)
权限修饰符
- 就是一些关键字,修饰成员,用于决定这些成员可以在什么位置被访问
- 这些关键字都是“封装”的体现形式
- 权限修饰符:
- private
- 只可以在本类中访问
- 默认的权限修饰符(啥都不写)
- 可以在本类中访问
- 可以在本包中的其他类中被访问
- protected
- 可以在本类中访问
- 可以在本包中的其他类中被访问
- 可以在其他包的子类中被访问
- public
- 可以在本类中访问
- 可以在本包中的其他类中被访问
- 可以在其他包的子类中被访问
- 可以在其他包的无关类中被访问
- private
多态 (polymorphic)
多态的概述
-
多态:按照字面意思:多种状态,在面向对象的语言中,接口的多种不同的实现方式即为多态。
Java中体现为子类对象的父类类型体现【子类类型充当父类类型的对象】
-
多态:事物的多种状态
- 对象的多态性:同一个对象,可能具有不同的名称,有不同的类型的引用指向它。本质:同一个物体有不同的名称和描述。
- 类型的多态性:同一个类型,可能具有不同的子类实现,同一个类型的引用,有不同的对象实现,
本质:同一个名字和描述,可以在不同的场景下有不同的真实实现。
-
多态的前提:
- 要有子父类的继承,或者实现的关系
- 父类的引用指向子类的对象
- 方法的重写(多态特点的一种体现)
多态中成员变量的访问特点
-
编译看左边,运行看左边
-
编译的时候,要看【=】左边是否有该变量的定义,如果有,就编译成功,没有就编译报错
运行的时候,要看【=】左边中对该变量的赋值,访问到的就是左边所属类型中对该变量的赋值。
多态中成员方法的访问特点
-
编译看左边,运行看右边
-
编译的时候,要看【=】左边是否有该方法的定义,如果有,就编译成功,没有就编译报错
运行的时候,要看【=】右边中所属的类型中,对该方法的具体实现,最终运行的是子类类型中对该方法的重写. 如果子类没有重写该方法就执行父类的方法。
向上向下转型
-
向上转型
正常情况:使用子类的引用指向子类的对象。
向上转型:使用父类的引用指向子类的对象 格式:父类类型 父类类型的对象名 = new 子类类型()
本质:缩小了子类的访问范围。减少了访问的权限(只能访问在父类中定义的内容)
-
向下转型:将指向子类对象的父类引用,恢复为子类的引用
格式:子类类型 子类类型的对象名 = (子类类型)父类类型的引用
本质:恢复子类类型原有的访问范围 (扩大了访问权限) -
注意:
向下转型,不是强制转换。
前提:就是对指向子类对象的父类引用,恢复成子类引用的概念。
多态的好处
-
提高了代码的扩展性
把不同的子类对象当做父类类型来看,可以屏蔽不同的子类对象之间的差异。写出通用的代码,做出通用的编程,以适应不同的 需求变化
-
在方法的参数列表中,可以定义父类类型的引用,将来调用的时候,对于父类类型下的所以的子类类型对象,都可以作为实际参数。
-
不在方法的参数列表中,就在普通的方法中,使用父类类型的引用指向子类的对象,也能提高代码的扩展性。对象的来源是特别广的,不仅仅是new出来的(还可以是通过反射获取,或者文件读取,或者网络传递,在写代码的的编译阶段,无法知道该对象具体的子类类型,需要使用父类类型的引用操作不知道子类类型的对象)。
抽象类(abstract class)
抽象方法
-
抽象:相同的,相似的的内容抽取出来
-
抽象方法:只有方法的声明,没有方法的实现的,就是抽象方法
在各个子类中,对于父类中的某个方法,分别都有自己不同的实现方式,在父类中就没有必须将该方法再进行实现。因此在父类中,只需要将方法的声明给定义出来,将来由该类的各个子类完成对该方法进行不同的实现
-
定义格式:
- 没有方法实现。连大括号都没有,直接在方法声明后面加上一个分号,表示方法的定义结束
- 为了标记该方法为抽象方法,需要在方法的前面加上一个abstract关键字
抽象类
-
可以定义抽象方法的类,就是抽象类
-
定义格式
abstract class 类名 {
}
抽象类的特点
-
抽象类和抽象方法都需要使用关键字abstract进行修饰
抽象类 abstract class {}
抽象方法 public abstract void test(); -
抽象类和抽象方法的关系:
抽象方法所在的类必须是抽象类
抽象类中未必一定定义的都是抽象方法,抽象类中可以有非抽象的方法 -
抽象类的实例化(抽象类能否创建对象)
抽象类不能直接进行实例化的,不能直接去创建对象
定义抽象类的子类类型,使用抽象类的子类类型去创建对象,调用方法 -
抽象类子类的前途
在子类中,将父类中所有的抽象方法都进行重写(实现),子类就成了一个普通类,就可以创建对象
子类没有将抽象父类中的所有抽象方法都进行重写,那么该子类就还是一个抽象类,还需要abstract关键字完成对该子类的修饰
抽象类的成员特点
-
成员变量:
可以定义变量,也可以定义常量的,但是不能被抽象。
-
构造方法:有
抽象类本身不能创建对象,但是抽象类是有子类的,将来子类可以创建对象,可以通过调用抽象父类中的构造方法,完成对私有成员变量的访问
一个类是否有构造方法,不取决于该类本身是否能够创建对象,而是取决于该类是否有成员变量,如果一个类中可以定义成员变量,那么就需要使 用构造方法完成对成员变量的初始化 -
成员方法:
既可以是抽象方法,也可以是非抽象方法
如果是抽象方法,对于子类而言,如果子类对所有的抽象方法都进行重写,该子类就是一个普通类
如果是非抽象的方法,将来子类就可以直接继承,进行访问
接口(interface)
接口的概述
-
广义:一切定义规则的都是接口
-
狭义:java中用于定义方法命名的规则就是接口
java接口中,全都是方法的声明,没有方法是实现。
-
好处:
一旦将命名规则定义出来,【方法调用】和【方法的实现】就可以进行分离,可以提高开发的效率,降低代码的耦合性
接口和多态的结合使用,也使得编程变得更加丰富多彩,充满变化。
接口的特点
-
接口的定义:使用interface关键字 ,编译之后也是生成一个.class字节码文件
interface 接口名称{
方法声明的定义;
} -
接口中,只可以声明抽象方法,只能定义给方法起名字的规则
-
类可以实现接口:使用implements关键字
实现:接口中只有方法名称定义,一个类实现之后,要将该接口中声明的规则进行一个真正的实现
-
接口的实例化:不能直接实例化的
定义实现类,实现接口,由实现类创建对象,对象调用方法
-
接口的实现类前途:
是一个抽象类,该类没有实现接口中所有的抽象方法
是一个普通类,该类实现了接口中所有的抽象方法
接口中成员的特点
-
成员变量:
只能是常量,不能是变量
默认加上public static final
建议手动加上 -
构造方法:
没有构造方法,接口中无法定义成员变量,所以不需要给成员变量初始化的赋值
虽然接口有自己的实现类,但是对于实现类而言,不去去访问接口中的构造方法的,而是访问实现类的父类的构造方法。 -
成员方法
可以是抽象的方法,也可以是非抽象的方法
如果是非抽象的方法,有一个前提,这个非抽象方法只能是默认方法或者静态方法(jkd1.8接口的新特性)
对于接口中的抽象方法,默认就加上public abstract
类与类,类与接口,接口与接口之间的关系
-
类与类
继承的关系,使用extends关键字
可以单继承,不可以多继承,可以多层继承 -
类与接口
实现关系使用implements关键字
可以单实现,也可以多实现
多实现的格式
class 实现类类名 implements 接口1,接口2…{
重写所有接口中的所有抽象方法;
} 在继承一个父类的前提下,还可以实现多个接口
格式
class 实现类类名 extends 父类 implements 接口1,接口2…{
重写所有接口中的所有抽象方法;
} -
接口和接口之间的关系
继承关系使用extends
可单继承,也可以多继承,还可以多层继承
多继承的格式
interface 接口名 extends 父接口1,父接口2…{
相当于继承了所有父类中的抽象方法
} -
类和接口的区别(设计区别)
继承:是不是
实现:有没有
小燕子,麻雀–》向上抽取一个父类,鸟类
飞机和鸟 --》有没有飞行的能力,可以将某个事物是否具备的能力,定义为一个接口
匿名内部类
-
没有名字的内部类
-
匿名内部类的使用前提
匿名类:继承某个类
匿名类:实现一个接口 -
格式:
new 父类类名或者接口名(){
父类方法的重写或者接口内容的实现。
}; -
匿名内部类本质:
- 创建了一个该类的子类类型的对象,或者是接口的实现类对象
- 更强调的是一种写法
- 不想去定义实现类但是必须要使用接口中的资源,就可以用匿名内部类