写在前面:⾯向过程与面向对象编程相比缺少了可重⽤性设计。
⾯向对象三⼤特征
一. 封装性:所谓封装,也就是把客观事物封装成抽象的类,并且类可以把⾃⼰的数据和⽅法只让可信的类或者对象操作,对不可信的进⾏信息隐藏。简⽽⾔之就是,内部操作对外部⽽⾔不可⻅(保护性)。
二. 继承性:继承是指这样⼀种能⼒:它可以使⽤现有类的所有功能,并在⽆需重新编写原来的类的情况下对这些功能进⾏扩展。
三 . 多态性:所谓多态就是指⼀个类实例的相同⽅法在不同情形有不同表现形式。多态机制使具有不同内部结构的对象可以共享相同的外部接⼝。(利⽤多态可以得到良好的设计)
类与对象的定义与使⽤
1. 类的概念:所谓的类就是指共性的概念,⽽对象指的是⼀个具体的、可以使⽤的事物。
2. 类的定义如下:
class 类名称 {
属性1;
属性2;
属性n...;
⽅法1(){}
⽅法2(){}
⽅法n(){}...
}
代码示例:
class Car{
String style;
int price;
public Car(String style,int price){
this.style = style ;
this.price = price ;
}
public String getCarInfo(){
return "款式:"+this.style+",价格:"+this.price;
}
}
由上面的定义可以看出,类由属性和方法组成。
属性:变量,描述每个对象的具体特点;
⽅法:操作的⾏为。
3. 由类产生对象:
语法如下:
类名称 对象名称 = new 类名称();
以上述Car类为例,产生如下一个Car类的对象(实例):
Car c = new Car();
Car c2 = new Car("Camaro",400000); //产生了一个“科迈罗”跑车,它的价格为400000
通过关键字 new,开辟了内存。
4. 对象内存分析:
首先我们先简单的理解Java中的内存区域为栈内存和堆内存两个区域。(实际很复杂)
栈内存(虚拟机局部变量表):存放的是局部变量。(包含编译期可知的各种基本数据类型、对象引⽤-即堆内存的地址,可以简单的理解为对象的名称)
堆内存:保存的是真正的数据即对象的属性。
通过上述的Car类,我们来分析一下内存中是怎么分配的:
首先:
Car c = new Car();
通过图片我们可以发现,对象实例保存在栈内存上,而属性和方法在堆内存上,上述代码只是通过new开辟了内存空间,并没有赋值,可以通过引用的方式为属性赋值,如下:
Car.name = "camaro";
Car.price = 400000;
如果此时再new一个Car类的对象c2,并让c2 = c,在内存上会发生什么呢?
我们可以看到,让c2 = c只会发生引用传递,即让两个对象指向同一个堆内存空间,而使得c2的堆内存成为垃圾内存空间。所以在以后的开发过程中,尽量控制好对象的产生数量,不产生无用的对象。
5. 关于对象的实例化:
对象的实例化操作实际上需要以下⼏个核⼼步骤:
- 进⾏类加载
- 进⾏类对象的空间开辟
- 进⾏类对象中的属性初始化(构造⽅法)
类的封装
用private关键字实现对类的封装,让内部操作对外部不可⻅。(对象不能直接操作属性)private实现封装的最⼤特征在于:只允许本类访问,⽽不允许外部类访。
问。
例如:
private String style;
private int price;
这样就对款式和价格这两个属性进行了封装,成为了私有属性。
如果要实现对私有属性的访问,有以下两种方法可以使用:
1.提供一系列的 setter() 和 getter() 方法;
public void setStyle(String s){
style = s ;
}
public String getStyle(){
return style;
}
public void setPrice(int p){
price= p;
}
public String getPrice(){
return price;
}
2.提供合适的构造方法。
public Car(String style,int price){
this.style = style ;
this.price = price ;
}
注意:我们在编写类时,类中的所有属性必须使⽤private封装。
上面提到了构造方法,那么构造方法有什么特点呢?
- ⽅法名称必须与类名称相同;
- 构造⽅法没有返回值类型声明;
- 每⼀个类中⼀定⾄少存在⼀个构造⽅法。(没有明确定义,则系统⾃动⽣成⼀个⽆参构造)
使用用构造方法初始化对象的属性进行初始化可以避免多次setter调⽤。
构造方法还可以进行重载,在于其参数的个数不同。有若干构造方法时,为了良好的代码风格,按照参数个数升序或者降序排列。
继承
继承的出现提高了代码的重用性。
继承的实现语法如下:
class ⼦类 extends ⽗类
子类被称为派生类,父类被称为超类。
继承的东西:继承了除了构造函数之外的其他的东西。
Java中只允许单继承,不允许多继承,但是可以多层继承。
在使用javap -c反汇编的时候我们会看到一些专有名词:
- invokespecial 代表构造函数
- invokevirtual 普通的方法 虚函数
- invokestatic 静态方法
重载(overload )和重写(override )
- overload 重载:函数名相同,参数列表不同,返回值不要求;
- override 重写:函数名相同,参数列表相同,返回值相同。
this和super关键字
这两个关键字有着很相似的地方,它们都可以访问属性、方法,但是也有本质上的区别,下面通过一张表给大家区分一下:
需要说明的一点,在使用this调用本类普通方法时,可以省掉this,但是加上可以更好地区分方法的定义来源;this和super调用构造方法时,必须放在构造方法的首行。
static关键字
static可以用来修饰属性和方法。
-
通过static修饰的属性称为类属性,保存在全局数据区内存中,所有对象都可以进行该数据区的访问。而普通属性保存在堆内存中,且每个对象独享属性。
-
访问static属性应该用类名称.属性名
-
所有的⾮static属性(实例变量)必须在对象实例化后使⽤,⽽static属性(类属性)不受对象实例化控制
-
使⽤static定义的⽅法,直接通过类名称访问
-
所有的static⽅法不允许调⽤⾮static定义的属性或⽅法
-
所有的⾮static⽅法允许访问static⽅法或属性
final关键字
final可以用来修饰类、⽅法、属性。
- final成员变量必须在声明的时候初始化或者在构造器中初始化,否则就会报编译错误
- 使⽤final定义的类不能有⼦类
- final⼀旦修饰⼀个类之后,该类的所有⽅法默认都会加上final修饰。(不包含成员变量)
- 使⽤final定义的⽅法不能被⼦类所覆写
- 使⽤final定义的变量就成为了常量,常量必须在声明时赋值,并且不能够被修改
- 使⽤final修饰的变量不能再次赋值
- 定义常量(public static final ),常量全⽤⼤写字⺟,多个单词间以_分隔。
代码块
根据代码块定义的位置和关键字分为普通代码块、构造块和静态块。
1.普通代码块:
{
... //直接使用{}定义
...
}
2.构造块:
class A{
{
...//定义在类中的代码块(不加修饰符)
}
}
3.静态代码块:
static{
...
}
注意:
- 构造块优先于构造⽅法执⾏,每产⽣⼀个新的对象就调⽤⼀次构造块,构造块 可以进⾏简单的逻辑操作(在调⽤构造⽅法前)
- 静态块优先于构造块执⾏
- ⽆论产⽣多少实例化对象,静态块都只执⾏⼀次
- 在主类中定义的静态块,优先于主⽅法(main)执⾏
内部类
内部类分为4种,分别是成员内部类(本地内部类)、静态内部类、方法内部类(局部内部类)、匿名内部类。
使用内部类创建内部类对象的语法:
外部类.内部类 内部类对象 = new 外部类().new 内部类();
Outter.Inner in = new Outter().new Inner();
在外部类内部创建内部类对象:
内部类 内部类对象 = new 内部类();
Inner in = new Inner();
内部类的特点:
- 内部类⽅法可以访问该类定义所在作⽤域中的数据,包括被 private 修饰的私有数据
- 内部类可以对同⼀包中的其他类隐藏起来
- 内部类可以实现 java 单继承的缺陷
- 对于⾮静态内部类,内部类的创建依赖外部类的实例对象,在没有外部类实例之前是⽆法创建内部类的
- 内部类是⼀个相对独⽴的实体,与外部类不是is-a关系
- 内部类可以直接访问外部类的元素(包含私有域),但是外部类不可以直接访问内部类的元素
- 外部类可以通过内部类引⽤间接访问内部类元素
- 成员内部类中不能存在任何static的变量和⽅法
- 静态内部类不可以使⽤任何外围类的⾮static成员变量和⽅法
- 局部内类不允许使⽤访问权限修饰符 public private protected 均不允许
- 匿名内部类是没有访问修饰符的,不能存在任何静态成员或⽅法,也没有构造方法
多态
对象多态性的核⼼在于⽅法的覆写。
- 通过对象的向上转型可以实现接收参数的统⼀,向下转型可以实现⼦类扩充⽅法的调⽤(⼀般不操作向下转型,有安全隐患)。
- 两个没有关系的类对象是不能够进⾏转型的,⼀定会产⽣ClassCastException。
- 静多态: 调用静态函数。
- 动多态: 运行时多态。