8 面向对象
概述:java编程语言是纯粹的面向对象思想的语言。
对象:类的实例化,即类中的个体
类:封装一些事物的状态数据并提供操作这些状态数据的方法(Method),实现逻辑。
类与对象的关系: 类是对象的模板,对象是类的实例化
8.1 程序开发的两个主流方法
(1)结构化程序设计,也叫面向过程程序设计
结构化程序设计主张以功能/步骤来进行软件设计。
如在解决问题时,主要是思考的是解决问题的步骤思路:
围棋游戏:
1.游戏开始-->2,黑棋执行-->3,绘制图片-->4,判断输赢--> 5,白棋执行-->6,绘制图片-->7,判断输赢-->8,返回2
-->9,游戏结束
结构化程序设计是以函数为程序单元 ,从一个函数开始,调用其他普通函数,进行一些数据处理,一直调用下去
设计思想:自顶而下,逐步精分,模块化
此种方式的编程语言有C,Basic,Pascal
(2)面向对象程序设计:
面向对象编程思想逐渐成熟起来。
它使用类,对象,继承,封装,消息等概念来进行程序设计。
主要从现实世界的客观事物(即对象)出发来进行开发软件系统,尽可能的运用人的自然思维方法 , 从客观事物来 思考问题, 认识问题。 从这些事物的本质特点,来抽象出系统所需要的类, 作为程序的基本单元。
围棋游戏:
黑白棋子及其行为,棋盘, 输赢规则
类是面向对象编程的基本单元,类含有一类事物的共同特征,
类可以产生多个对象,这些对象构成了程序中所需要的数据。
此类方法的编程语言:C++,java,C#, Ruby
举例:
完成一个“猪八戒吃西瓜”这样的逻辑
面向过程: 函数吃 是核心----> 吃(猪八戒,西瓜)
面向对象: 一类事物的一个:猪八戒,吃是猪八戒的行为;一类事物的一个:西瓜; 猪八戒.吃(西瓜)
组装电脑:
如果是主板,硬盘,内存条这样的大件,容易组装---面向对象
如果是二极管,三极管,电路板这样的小件进行组装,很难 ---面向过程
面向对象要比面向过程的粒度要大,相对来说容易,简单。
面向过程要比面向对象的性能要高,因为面向对象要进行对象的实例化,
比较吃内存,吃性能。
8.2 类
类:封装一些事物的状态数据并提供操作这些状态数据的方法(Method),实现逻辑。
成员变量(状态数据)+方法(行为)定义=类的定义
抽象数据类型:用一堆不同类型的状态数据来描述的一种新的事物。
用状态数据(成员变量)总结一类事物的共同特征,
用方法总结一类事物的共同行为。
类的定义: =成员变量+方法格式语法:
修饰词 class 类名{
type field1;
type field2;
.....
修饰词 返回值类型 method1(形参){}
修饰词 返回值类型 method2(形参){}
.....
}
=============================================================================================
定义一个类型:Person
成员变量(状态数据-共同特征):
name,gender,age,birth,idcard,isMarry
方法(行为)
eat()
sleep()
work()
sport()
对象:类的实例化,即类中的个体
如何创建对象:使用关键字new
类名 引用变量 = new 类名();
引用变量,简称引用,存储的是对象的地址信息成员变量的调用:引用.成员变量名
方法的调用: 引用.方法名(有参传参)=============================================================================================
this关键字:
在普通方法中,操作的成员变量如果与局部变量名称相同时,为了避免出现歧义,应该在成员变量
前使用this.进行区分,当没有歧义时,this.可以省略不写
在构造器中:还可以使用this关键字调用本类中的其他构造方法。
语法格式:this(有参传参)
只能在构造器中的首行首句上使用
8.3 内存管理机制
jvm将其管理的内存分成三大主要区域:方法区,堆,栈
方法区:当程序运行时,将所需要的class文件(类的信息)加载到此区间
堆: 用于存储引用类型所产生的对象,如果对象有成员变量,会给成员变量分配空间
栈:方法内的变量(局部变量)是在栈中开辟的
基本类型的变量与引用类型的变量:
值传递:
基本数据类型的变量里存储的是基本类型的数据,
int a = 5;
int b = a;//将a里的5复制了一份,传递给了b
址传递:
引用类型的变量里存储的是对象的地址信息
Car c = new Car();
Car d = c;//将c里的地址信息复制了一份,传递给了d
成员变量与局部变量的区别:
成员变量:
定义位置:在方法外,类体中
默认值:有默认值,构造器中可以不对成员变量初始化
内存位置:在堆中
生命周期:从对象实例化开始出现,到对象消失
局部变量(形参也是局部变量):
定义位置:在方法内(包含小括号内的形参)
默认值:是没有默认值,必须初始化再使用
内存位置:在栈帧中
生命周期:从声明时开始,到方法结束后,栈帧消失时。
空指针异常:
java.lang.NullPointerException
在程序运行时,可能会出现此异常,出现的原因:
Car c = new Car();
System.out.println(c.color);//0
Car c = null;
System.out.println(c.color);//就会出现空指针异常
null:是引用类型的默认值
垃圾回收机制:(GC)
jvm的一个独有线程(程序),用于回收没有任何引用指向的对象。
System.out.println((new Person()).name);
System.out.println((new Person()).name);
上述产生的对象以后再也无法使用,如果类似的这种情况有很多,对象来不及被处理,内存剩余
空间就会越来越小,有可能出现内存溢出情况。
因此需要一个处理机制,即垃圾垃圾回收机制。没有被引用的对象会被视为垃圾,等待被GC回收。
(因为有垃圾回收机制,所以java程序员无需担心内存溢出或泄露情况)
程序示例:
先进行Person类的创建,包括了对象name,gender,age,birth,idcard,isMarry的创建以及eat(),work(),sport()等方法的创建:
package com.hyxy.oop.day01;
public class Person {
String name;
char gender;
int age;
String birth;
String idcard;
boolean isMarry;
public void eat(String food) {
System.out.println(name+"喜欢吃"+food);
}
public void sleep() {
System.out.println("生日是"+birth+"的,性别是"
+gender+",名字叫"+name
+"喜欢睡觉");
}
public void work() {
System.out.println(name+"特别喜欢上班");
}
public void sport() {
System.out.println("婚姻状态为"+isMarry+"的"
+ "女孩特别喜欢去健身房");
}
/**用来描述成员变量*/
public String info() {
String str = "[name="+name+
",gender="+gender+
",age="+age+
",birth="+birth+
",idcard="+idcard+
",isMarry="+isMarry+"]";
return str;
}
}
对Person进行对象的实例化以及方法的调用:
package com.hyxy.oop.day01;
public class TestPerson {
public static void main(String[] args) {
/* 创建一个名叫张三的对象*/
Person p = new Person();//创建对象后,成员变量有默认值
String info = p.info();//方法的调用
System.out.println(info);
/* 成员变量的调用*/
p.name="张三";
p.gender = '男';
p.age = 23;
p.birth = "1995-11-11";
p.idcard = "222222199511110000";
p.isMarry = true;
String info1 = p.info();
System.out.println(info1);
p.eat("面包");
p.sleep();
p.work();
p.sport();
//创建一个叫李四的女人
Person p1 = new Person();
p1.name = "李四";
p1.gender = '女';
p1.age = 18;
p1.birth = "2000-11-11";
p1.idcard = "000000000000000000";
p1.isMarry = true;
p1.eat("大米");
p1.sleep();
p1.work();
p1.sport();
System.out.println("p1的详细信息:"+p1.info());
}
}
8.4 继承
继承: 现实世界中通过分析多种类型,然后发现有一些共同特征和共同行为,再将这些种类归纳为一种新类型。
子类可以从父类中继承一些成员变量和方法。子类还可以添加自己的独有成员变量和方法。
子类:也叫派生类
父类:也叫超类,基类计算机语言中:先编写父类型,再编写子类型,然后再有对象
关键字extends,用于继承语法
格式:
public class subClass extends SuperClass{
}
优势:提高复用性,减少代码的冗余,提高开发效率
继承中的构造器:
子类不能继承父类的构造器,但是子类中的构造器可以调用父类的构造器
语法: super(有形参传实参);
作用:可以更好的给继承过来的成员变量赋值
PS:子类中的构造器一定会有一个调用了父类的构造器,父类中如果没有无参构造器,子类需要显示调用父类构造器
如果父类中有无参构造器,子类中的构造器可能隐式调用了父类的无参构造器,即:隐藏了super()
super()与this()的区别:
相同点:都是调用构造器,而且必须放在首行首句
不同点:super()是调用父类的构造器
this()是调用本类中其他构造器
继承的特点:
传递特性:B类型继承了A类型的特征,C类型继承了B类型的特征。C类型间接继承了A类型的特征。 单一继承性:一个子类只能继承一个父类。但是一个父类可以有多个子类。
8.5 Object
Object:是所有引用类型的顶级父类。系统都会默认使引用类型继承extends Object。
此类中提供了常用的方法:
1:toString():
在Object中,返回的是类全名@HashCode值(16进制写法),即对象的内存堆中的位置信息。
【类有类名和类全名之分:
类名:即最短的名称
类全名:从包名开始写的名称
如:String 是类名 java.lang.String是类全名 】
此方法会在输出变量时,或引用变量进行拼接时默认调用。而查看地址信息,通常没有必要,我们通常要查看的是对象的成员变量,因此我们都需要重写toString()方法,用于查看对象的详情。
格式:"[成员变量1="+成员变量1+",成员变量2="+成员变量2+"]"2:equals(Object obj)
Object类型中的此方法中的逻辑是比较调用者this与形参obj的地址信息是否相等。
简单说成:比较this与obj是不是同一个对象
所以在定义类型时,继承过来的equals方法我们要重写。
重写规则:
(1):查看传进来的obj是不是null
if(obj == null){
return false;
}
(2):查看传进来的obj是不是this.
if(obj == this){
return true;
}
(3):查看传进来的obj是不是本类型
if(obj.getClass()!=this.getClass()){
return false;
}
可以改成if(!(obj instanceof Person)){
return false;
}instanceof关键字:
作用是判断引用变量指向的对象是否属于某一类型
语法:
boolean变量名 instanceof 类型名
8.6 package:包
作用:用于管理源文件,区分类全名。
命名规则:域名后缀.域名.项目名.模块名
声明位置:在源文件的首行首句。常用的包:
java.lang.*,因为里面的类型非常常用,因此不需要导包。用*表示所封装的类
java.util.*,此包下封装了很多常用的工具类
java.io.*,此包下封装了io流的类型
java.net.*,此包下封装了很多关于网络的多种类型
import:导包关键字:
在class之上,package之下。
用于声明类的类全名,在逻辑中就可以使用短的类名。
优点:可以减少代码的编写量。
8.7 修饰词(public、protected、default、private)
修饰词:private,protected,public,默认的(default)
修饰类时:
外部类:可以使用public和默认的
内部类:可以使用public,protected,默认的,private
修饰成员变量:四个都可以进行修饰
可见性不一样:
同类中 同包下 不同包子类 其他
public true true true true
protected true true true false
default true true false false
private true false false false
在实际开发中,成员变量要尽可能的设置成不可见 好处是:提高代码的安全性。即用private修饰为了在其他类中可以对成员变量进行重新设置值或者获取值, 我们可以定义相应成员变量的共有方法来进行操作。
public void setName(String name){
this.name = name;//修改成员变量的值
}
public String getName(){
return name;
}
修饰方法:与修饰成员变量的可见性一致。
方法的重写:子类不能重写父类的私有方法
修饰词final:最终的,最后的
(1)修饰类:被final修饰的类不能再有子类了。
(2)修饰成员变量:可以直接初始化,也可以在构造器中初始化
不能在其他任何地方再次赋值。
修饰局部变量只能初始化一次。
(3)修饰方法:被final修饰的方法,不能在子类中重写 。
8.8 static修饰词(静态)
1、修饰成员变量:
(1)修饰的成员变量,不属于对象的数据结构。
(2)静态变量属于类的,通常使用类名去调用。
(3)静态变量与类的信息一起存在方法区中,只存在一份,是对象的公共资源。
2、修饰方法:
(1)通常的方法都是与具体对象有关系,即对象的方法(行为)
(2)如果是static方法则与对象毫无关系,是类的方法,通常用类名去调用
常常与参数有关系。
(3)static方法内因为无法使用this关键字,因此不能直接访问非静态成员
(4)static方法的作用一般都用于设计成“工具方法”和“工厂方法”
如:Arrays.sort(数组名);
Math.random();
Math.sqrt(参数);
Math.abs(参数);
Math.sin(参数);3、static修饰代码块
static{
代码逻辑
}
静态块存储在方法区中,只加载一次,与对象无关。
执行时机为加载类的信息期间,可以理解为在实例化之前。
作用:通常用于加载程序中所需要的静态资源:如,图片,音频,视频等
8.9 抽象类
抽象类:
因为继承的关系,子类的功能可以越来越具体,相对来说父类更加一般化,通用化。因为父类封装了子类的共同行为, 所以,在定义时,我们可以定义方法的逻辑。有的时候,在父类中无法完成子类的功能需求,此时,不如不写,即不写 方法体,代码简单化。但是此方法必须使用abstract修饰,那么此类型也必须使用abstract声明或修饰,即抽象类。
1、抽象方法与抽象类
1)用abstract修饰的方法,不需要写方法体,但是需要用分号来结尾。
2)有抽象方法的类必须使用abstract声明(修饰)
2、抽象类不能实例化
1)抽象类不能创建对象,没有意义
2)抽象类可以提供构造器
3)抽象类中可以没有抽象方法
4)abstract和final不能同时修饰一个类型
3、继承抽象类
1)若一个类继承了抽象类,那么这个类必须重写(实现)抽象类的所有抽象方法。
2)若一个类没有实现抽象类的所有抽象方法,那么这个类必须使用abstract修饰。
4、抽象类的意义:
1)给不同的子类定义一个父类
2)抽象类可以封装子类的共同特征与共同行为
3)虽然子类方法可能有不同的实现逻辑,但是方法的定义一致