对象,类和引用类型
- 什么是类?什么是对象?
- 现实世界是由很多很多对象组成的
基于对象抽出了类 - 对象:真实存在的单个的个体
类:类别/类型,代表一类个体 - 类中可以包含:
3.1)所有对象所共有的属性/特征-------成员变量
3.2)所有对象所共有的行为------------方法 - 一个类可以包含多个对象,同一个类的多个对象,结构相同,数据不同
- 类是对象的模板,对象是类的具体的实例
- 名词解释
- 抽象数据类型:将不同类型的数据的集合组成一个整体来描述一种新的事物
- 类定义了抽象数据类型的组成(成员变量),同时定义了对其实施的操作(方法)
- 如何创建类?如何创建对象?如何访问成员?
- 类的定义
class 类名{ 成员变量, 方法}
对象- 使用new关键字创建对象,创建对象的过程也称为类的实例化
- 引用类型变量
- 简称“引用”,因其存放的是对象的地址,存储在栈中,指向存储在堆中的对象- 可以通过“引用”对对象进行操作,通过“引用. ”来访问成员变量和调用方法
- 除8中基本数据类型之外,其他类型的变量都称为引用类型变量
- 引用类型变量之间赋值赋的是地址
- 引用可以赋值为null,表示没有指向任何对象。因此通过空的引用访问成员变量和方法会产生NullPointerException
- 数组是引用类型
- java中,数组属于引用数据类型
- 数组对象在堆中存储,数组变量属于引用,存储数组对象的地址,指向数组对象。数组的元素可以看成数组对象的成员变量,只不过类型全部相同 - 引用类型数组
- 声明,如:Cell[] cells = new Cell[4]
new Cell[4]仅仅是分配了4个空间,并没有创建对象
默认初始值为null
需使用new为每个数组元素初始化。如cells[0] = new Cell();
数组的元素也是数组
即二维数组,但java中并无此概念
通常用来表示多行多列的情况
- 引用类型之间画等号
- 指向同一个对象
- 对其中一个引用的修改会影响另一个引用
- eg:房子钥匙
- 基本类型之间画等号
1)赋值
2)对其中一个变量的修改不会影响另一个变量- eg:身份证复印件
- null:空,没有指向任何对象
若引用的值为null,则该引用不能再进行任何操作了
若操作则NullPointerException空指针异常
####方法的签名:方法名+参数列表 - 方法的重载(Overload):
- 在同一个类中,方法名称相同,参数列表不同
- 编译器在编译时(语法检查)自动根据签名来绑定调用的方法
- 构造方法:
- 常常用于给成员变量赋初值
2) 与类同名,没有返回值类型,但也不能写void - 在创建对象时被自动调用
- 若自己不写构造方法,则编译器默认一个无参构造方法
若自己写了构造方法,则不再默认提供 - 构造方法可以重载
6) 意义:初始化成员变量
- this:指代当前对象,哪个对象调方法指的就是哪个对象
只能用在方法中,方法中访问成员变量之前默认有个this.
构造方法中参数名与成员变量名相同时,this是必须的
this的用法:- this.成员变量名-------------访问成员变量
- this.方法名()---------------调用方法
- this()----------------------调用构造方法
- 引用类型数组:
1) Cell[] cells = new Cell[4]; //创建Cell数组对象
cells[0] = new Cell(2,5); //创建Cell对象
cells[1] = new Cell(2,6);
cells[2] = new Cell(2,7);
cells[3] = new Cell(3,6);
2) Cell[] cells = new Cell[]{
new Cell(2,5),
new Cell(2,6),
new Cell(2,7),
new Cell(3,6)
};
3) int[][] arr = new int[3][];
arr[0] = new int[2];
arr[1] = new int[3];
arr[2] = new int[2];
arr[1][0] = 100; //给arr中第2个元素中的第1个元素赋值为100
4) int[][] arr = new int[3][4]; //3行4列
for(int i=0;i<arr.length;i++){
for(int j=0;j<arr[i].length;j++){
arr[i][j] = 100;
}
}
##二、对象的内存管理,继承,访问控制,static和final
- 内存管理:由JVM来管理---------了解
- 堆:
1) 存储所有new出来的对象(包括成员变量)
2) 垃圾:没有任何引用所指向的对象
垃圾回收器(GC)不定时到内存中清扫垃圾,
回收过程是透明的,并不一定一发现垃圾,则立即回收,
调用System.gc()可以建议JVM尽快调度GC来回收垃圾
3) 内存泄漏:不再使用的内存还没有被及时的回收
严重的泄漏会导致系统的崩溃
建议:对象不再使用时及时将引用设置为null
4) 成员变量的生命周期: 创建对象时被存储在堆中,对象被回收时一并被回收 - 栈:
1) 存储正在调用的方法中的所有局部变量(包括参数)
2) 调用方法时在栈中为该方法分配一块对应的栈帧,
栈帧中存储方法的局部变量以及参数,
方法执行结束则栈帧被清除,局部变量一并被清除
3) 局部变量的生命周期:调用方法时被存储在栈中,方法结束时与栈帧一并被清除 - 方法区:
1) 用于存储.class字节码文件(包括方法)
2) 方法只有一份,通过this来区分具体的对象
- 继承:
- 作用:实现代码的复用
- 通过extends来实现继承
- 父类:所有子类所共有的属性和行为
子类:子类所特有的属性和行为 - 子类继承父类后,子类具有:子类的+父类的
- 一个父类可以有多个子类,
一个子类只能有一个父类-------单一继承 - 继承具有传递性
- java规定: 构造子类之前必须得先构造父类
子类构造方法中若没有调用父类的构造方法,
则默认super()调用父类的无参构造方法,
若子类构造方法中调用父类构造方法了,则不再默认提供
super()调用父类构造方法必须位于子类构造的第一句
-
super: 指代当前对象的父类对象
super的用法:
1)super.成员变量名---------访问父类的成员变量
2)super.方法名()-----------调用父类的方法
3)super()------------------调用父类的构造方法 -
向上造型:
1) 父类型的引用指向子类对象
2) 能点出来什么,看引用的类型 -
成员变量:
1)存在类中,方法外
2)创建对象时存在堆中,对象被回收时一并被回收
3)有默认值,可不显示初始化 -
局部变量:
1)存在方法中
2)调用方法时存在栈中,方法调用结束时被清除
3)没有默认值,必须自行设立初始值 -
方法的重写(Override):重新写、覆盖
- 发生在父子类中,方法名称相同,参数列表相同,方法体不同
- 重写方法被调用时,看对象的类型
重写需遵循"两同两小一大"规则: - 两同:
1) 方法名相同
2) 参数列表相同 - 两小:
1) 子类方法的返回值类型小于或等于父类的
1.1) void时,必须相等
1.2) 基本类型时,必须相等
1.3) 引用类型时,小于或等于
2) 子类方法所抛出的异常小于或等于父类的—异常之后 - 一大:
子类方法的访问权限大于或等于父类的----访问控制修饰符后
- 重写与重载的区别:----------常见面试题
- 重写(Override):
1) 发生在父子类中,方法名称相同,参数列表相同
2) 遵循"运行期绑定",看对象的类型调用方法 - 重载(Overload):
1) 发生在一个类中,方法名称相同,参数列表不同
2) 遵循"编译期绑定",看参数的类型绑定方法
- package:
1) 作用:避免类的命名冲突
2) 包名可以有层次结构
3) 同包中的类不能同名,类的全称:包名.类名
4) 包名建议:所有字母都小写
import:
1) 同包中的类可以直接访问,
不同包中的类不能直接访问,想访问:
1) 先import声明类,再直接访问类-----建议
2) 类的全称-----------------太繁琐,不建议 - 访问控制修饰符:
- public:公开的,任何类
- private:私有的,本类
3)protected:受保护的,本类、子类、同包类 - 默认的:什么也不写,本类、同包类
5) 类/接口的访问修饰符只能是public和默认的
类中的成员的访问修饰符如上4种都可以
6)修饰类
- public修饰的类可以被任何一个类访问
- protected和private可以用于修饰内部类
- static:静态的
- 静态变量:
1) 由static修饰
2) 属于类的,存在方法区中,只有一份
3) 常常通过类名点来访问
4) 何时用:所有对象所共享的数据(图片、音频、视频等) - 静态方法:
1) 由static修饰
2) 属于类的,存在方法区中,只有一份
3) 常常通过类名点来访问
4) 没有隐式的this传递,所以在静态方法中不能直接访问实例成员
5) 何时用:方法的操作仅与参数相关而与对象无关 - 静态块:
1) 由static修饰
2) 在类被加载期间自动执行,因类中被加载一次,所以静态块也只执行一次
3) 何时用:加载/初始化静态资源(图片、音频、视频等)
- final:最终的、不可改变的
- 修饰变量:变量不能被改变
修饰成员变量,两种方式初始化:
声明同时初始化
构造函数中初始化
修饰局部变量
使用之前初始化即可
2)常常同final一起修饰常量
如:public static final int NUM=100;
常量需在定义时同时初始化,建议都大写
常量会在编译期被替换
3) 修饰方法:方法不能被重写
4) 修饰类:类不能被继承
- static final常量:
1) 必须声明同时初始化
- 通过类名点来访问,不能被改变
- 建议:常量名所有字母都大写,多个单词用_分隔
4) 编译器在编译时常量被直接替换为具体的值,效率高
##三、抽象类、接口和内部类
2. 抽象方法:
- 由abstract修饰
- 只有方法的定义,没有方法的具体实现(连{}都没有),用一个“;”结束
- 抽象类:
- 由abstract修饰
- 包含抽象方法的类必须是抽象类,不包含抽象方法的类也可以声明为抽象类—我乐意
- 抽象类不能被实例化
- 抽象类是需要被继承的,子类:
4.1) 重写所有抽象方法-------常用
4.2) 也声明为抽象类---------不常用 - 抽象类的意义:
5.1) 封装子类所共有的属性和行为-------代码复用
5.2) 为所有子类提供一种统一的类型-----向上造型
5.3) 特别意义:可以包含抽象方法,为所有子类提供一个统一的入口
子类可以有不同的实现,但方法的定义是一致的
抽象类实例:
//求一组图形中的最大面积
public class ShapeTest {
public static void main(String[] args) {
//Shape s = new Shape(); //编译错误,抽象类不能被实例化
Shape[] shapes = new Shape[4]; //创建Shape数组对象
shapes[0] = new Circle(1); //向上造型
shapes[1] = new Circle(2); //大
shapes[2] = new Square(1);
shapes[3] = new Square(2);
maxArea(shapes);
}
public static void maxArea(Shape[] shapes){
double max = shapes[0].area();
int maxIndex = 0; //最大面积下标
for(int i=1;i<shapes.length;i++){
double area = shapes[i].area();
if(area>max){
max=area;
maxIndex=i;
}
}
System.out.println("最大面积为:"+max+",所在下标为:"+maxIndex);
}
}
abstract class Shape{ //抽象类
protected double c; //周长
public abstract double area(); //抽象方法
}
class Circle extends Shape{
public Circle(double c){
this.c = c;
}
public double area(){ //重写抽象方法
return 0.0796*c*c; //0.0625
}
}
class Square extends Shape{
public Square(double c){
this.c = c;
}
public double area(){
return 0.0625*c*c;
}
}
- 接口:
- 是一个标准、规范--------制定方,遵守了这个标准,就能干某件事(造型)
- 接口是一种数据类型(引用类型)
- 由interface定义
- 只能包含常量和抽象方法
- 接口不能被实例化
- 接口是需要被实现的,实现类:
必须重写接口中的所有抽象方法 - 一个类可以实现多个接口,用逗号分隔,若又继承又实现时,应先继承后实现
8) 接口可以继承接口
4. 抽象类和接口的区别:
1) 一个类只能继承一个抽象类,但可以实现多个接口。
2) 抽象类中可以包含抽象方法和非抽象方法,而接口中的所有方法均为抽象的。
3) 子类继承抽象类必须实现抽象类中所有抽象方法,否则子类也必须是抽象类。
4) 而子类实现接口则必须实现接口中的所有抽象方法。
- 多态:
- 多态的意义:
1.1) 同一类型的引用,指向不同的对象时,有不同的实现
-----行为的多态:cut(),run(),study(),teach()…
1.2) 同一个对象,被造型为不同的类型时,有不同的功能
-----对象的多态:我,水… - 向上造型:
2.1) 父类型的引用指向子类的对象
2.2) 能造型成为的类型有: 父类+它所实现的接口
2.3) 能点出来什么,看引用的类型 - 强制类型转换,成功的条件只有两种:
3.1) 引用所指向的对象,就是该类型
3.2) 引用所指向的对象,实现了该接口 - 若不符合如上两个条件,则发生ClassCastException类型转换异常
建议:在强转之前先通过instanceof判断引用指向的对象是否是某种类型
####成员内部类: 应用率不高 - 类中套类,外面的称为外部类Outer,里面的称为内部类Inner
- 内部类通常只服务于外部类,对外不具备可见性
- 内部类对象通常是在外部类中创建的
- 内部类中可以直接访问外部类的成员(包括私有的)
内部类中有一个隐式的引用指向了创建它的外部类对象
eg: 外部类名.this
5) 代码说明
外部类:
public class Outer {
private int time; private Inner inner; Outer(int time){
this.time=time; inner=new Inner(); inner.timeInc();
}
public void printTime(){ System.out.println(time);
}
内部类:
class Inner{
public void timeInc(){ time++;
}}}
测试代码:
Outer out=new Outer(100); out.printTime();
输出应为 101
此例中 timeInc()的完整调用应为 Outer.this.timeInc():
其中 Outer 是一个隐式的引用,指向对象 out this 也是一个隐式的引用,指向对象 inner。
####匿名内部类:
- 若想创建一个类(子类)的对象,并且对象只被创建一次,
此时该类可以不必命名,称为匿名内部类 - 匿名内部类中若想访问外部的变量,该变量必须是final的
3) 所有类都有独立的.class。包括内部类
接口:
public interface Action { public void execute();
}
父类:
public class Super { public void print(){
System.out.println("hello");
}}
匿名内部类:
public class AnonymousInnerClass {
Action action=new Action(){ public void execute(){
System.out.println("Hello, World");
}};
Super sup=new Super(){ public void print(){
System.out.println("Hello, My World");
}};
public void execute(){ action.execute();
}
public void print(){ sup.print();
}}
测试类:
public static void main(String[] args) {
AnonymousInnerClass test=new AnonymousInnerClass(); test.execute();
test.print();
}
##面向对象三大特征:
- 封装:
- 类: 封装的是对象的属性和行为
- 方法: 封装的是具体的业务逻辑功能实现
- 访问控制修饰符: 封装的是访问的权限
- 封装的意义:
对外提供可调用的,稳定的功能
封装容易变化的,具体的实现细节,外界不可访问。其意义在于:
降低代码出错的可能性,便于维护
当内部的实现细节改变时,只要保证对外的功能定义不变,其他模块就不会因此而受到牵连
- 继承:
- 代码的复用
- 父类/基类:所有子类所共有的属性和行为
子类/派生类:子类所特有的属性和行为 - 子类继承父类之后,子类具有:父类的+子类的
- 传递性、单一继承、多接口实现
- 多态:
- 意义:行为的多态、对象的多态
- 向上造型、强制类型转换、instanceof判断
- 多态的表现形式:
3.1) 重写(依据对象的不同来实现多态)
3.2) 重载(依据参数的不同来实现多态)
##面向对象设计规则
- 将公共的属性和行为抽象到父类中
- 所有子类行为一样,写成普通方法;子类行为不一样,写成抽象方法。(如有抽象方法,则可确定为抽象类)
- 符合既是也是原则,使用接口(意即需要继承多个父类时)