1 面向对象编程简介
1.1 面向对象编程的三大特征
- 封装性:将客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或对象操作,对不可信的进行信息隐藏。 简而言之就是,内部操作对外部而言不可见(保护性)。
- 继承性:可使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
- 多态性:一个类实例的相同方法在不同情形下有不同表现形式。 多态机制使具有不同内部结构的内部对象可以共享相同的外部接口。
1.2 面向对象名词扩展
- OOA:面向对象分析
- OOD:面向对象设计
- OOP:面向对象编程
2 类与对象的定义与使用
2.1 基本概念
所谓的类就是指共性的概念,而对象指的是一个具体的、可以操作的事物。
首先产生类(类是生产对象的蓝图),而后才可以产生对象。对象的所有行为,一定在类中进行了完整的定义。
类的组成:
- 方法(操作的行为)
- 属性(变量,描述每个对象的具体特点)
2.2 定义与使用
定义类的语法如下:
class 类名称{
属性1;
属性2;
属性n...;
方法1(){}
方法2(){}
方法n(){}...
}
如上便是类的一个类的完整定义,此时的方法不再是由主类直接调用,而需要对象调用。
例:Student类的定义
有了类(蓝图),我们就可以定义(生产)对象了。
生产对象的语法:
类名称 对象名称 = new 类名称();
以Student为例,我们可以产生一个Student类的实例(对象)
Student stu = new Student("Tom",20);
通过对象调用实例变量与实例方法
Student stu = new Student("Tom",20);
System.out.println(stu.getInfo());
注意:只要出现了关键字new,就开辟了内存。
Java中,所谓的性能调优,调整的就是内存问题。
2.3 对象内存分析
- 栈内存(虚拟机局部变量表):存放局部变量(编译时期基本数据类型、堆内存地址,即对象的名称),Java栈是与线程对应起来的,每当创建一个线程,JVM就会为这个线程创建一个对应的Java栈。
- 堆内存:保存真正的数据。即对象的属性信息。
我们可以通过代码和内存分析图来清楚的分析这两部分内存。
main方法中第一行出现了关键词new,表明在堆上分配了内存,并产生了Student类的对象stu引用这部分内存。
经过下面两句代码:
stu.name = "李四";
stu.age = 20;
通过stu引用设置堆中属性值,内存图变化如下:
对象(引用数据类型)必须在实例化以后调用,否则会产生"NullPointerException"(运行时错误),编译时不会出错。
NullPointerException:此类异常只有引用数据类型(数组、类、接口)才会产生,以后出现此类异常,就根据出错位置查看引用类型变量是否初始化。
2.4 引用传递分析
本质:一块堆内存可以被多个栈内存所指向
Student stu1 = new Student();
Student stu2 = new Student();
stu2 = stu1;
执行上述代码后的内存分析图如下:
垃圾内存:没有任何栈指向的堆内存空间。
所有的垃圾空间会不定期GC,GC会影响性能,所以在开发中一定要控制好对象的产生数量。
3 封装和构造方法
3.1 private实现封装
若类中属性直接暴露给兑现对象操作,存在不安全问题(由于对象可以直接操作类中属性,无法限制属性是否正常设置)
就比如说一个类中的工资属性,若没有实现private封装,则可能被人为修改,引起一些不必要的麻烦。
- 使用private修饰的属性或方法,表示该属性与方法只能在本类中直接使用。
- 若private封装的属性要被类的外部访问,需要提供setter(设置属性值)/getter(取得属性值)方法。
class Student{
private String name;
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void getInfo(){
System.out.println("该学生姓名是:"+this.name+",年龄为:"+this.age);
}
}
public class Test{
public static void main(String[] args) {
Student stu = new Student();
stu.setName("小明");
stu.setAge(20);
stu.getInfo();
}
}
private封装的特点:只允许本类访问,而不允许外部类访问。
3.2 类的设计原则
- 编写类时,类中的所有属性必须使用private封装。
- 属性若要被外部访问,必须定义setter、getter方法。
3.3 构造方法
观察产生对象的方法:
(1)类名称 (2)对象名称 = (3)new (4)类名称();
分析上述产生对象的方法:
- (1)任何对象都应该有其对应的类,类是对象的蓝图;
- (2)是一个唯一的标记,引用一块堆内存;
- (3)表示开辟新的堆内存空间;
- (4)构造方法。
构造方法要遵循的原则:
- 方法名称必须与类名相同
- 构造方法没有返回值类型声明
- 每一个类至少存在一个构造方法(若没有明确定义,则系统自动生成一个无参构造)
若类中定义了构造方法,则默认的无参构造将不再生成。
例:使用构造方法设置对象属性。
class Student{
private String name;
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Student(String name,int age) {
this.name = name;
this.age = age;
}
public void getInfo(){
System.out.println("该学生姓名是:"+this.name+",年龄为:"+this.age);
}
}
public class Test{
public static void main(String[] args) {
Student stu = new Student("王五",22);
stu.getInfo();
}
}
构造方法的调用和对象内存分配几乎是同步完成的,我们可以利用构造方法来对类中的属性进行初始化操作(可以避免多次setter调用)。
3.3 构造方法重载
public Student(){
System.out.println("这是一个无参构造...");
}
public Student(String name,int age) {
this.name = name;
this.age = age;
}
3.4 匿名对象
new Student("张三",18).getInfo();
由于匿名对象不会有任何的栈空间的指向,所以使用一次就成为垃圾空间。