Java面向对象
思想概述
面向过程
面向过程,其实就是面向着具体的每一个步骤和过程,把每一个步骤和过程完成,然后由这些功能方法相互调用,完成需求。
- 例如:吃煎饼果子利用面向过程的思想:
- 学习摊煎饼的技术
- 买材料鸡蛋,油,葱等等
- 开始摊
- 吃
- 收拾
特点
- 强调的是过程,所有事情都需要自己完成
- 代表语言:C语言
面向对象
面向对象思想就是不断的创建对象,使用对象,指挥对象做事情。(如果有对象,直接用对象,对我们直接提供服务)
- 例如:吃煎饼果子利用面向对象的思想
- 找会摊煎饼的大妈(创建一个摊煎饼大妈的对象)
- 调用其摊煎饼的技能(功能),传递进去钱参数
- 返回给我们一个煎饼
- 吃
特点
- 强调的是对象,然后由对象去调用功能
- 面向对象的程序是由对象组成的,每个对象包含对用户公开的特定功能部分和隐藏的实现部分
- 适合解决规模较大的问题
类和对象
概述
Java中最基本的单位是类,类和对象是用来描述现实世界的事物的:
属性 :就是该事物的描述信息(事物身上的名词)
行为 :就是该事物能够做什么(事物身上的动词)
类和对象的关系
- 类是构造对象的模板和蓝图,可以将类想像成制作小甜饼的模具,而对象是小甜饼
- 类是相对于具体事物的抽象概括
- 由类构造对象称为:创建类的实例
(例如:车是一个类,具体的开的奔驰、宝马,就是对象)
类
类是一组相关的属性和行为的集合
(例如:我们班所有的同学都具备相同的属性和行为,比如:姓名,年龄,学习,这样就把所有的学生成为学生类)
属性:事物的属性特征用成员变量来定义
意味着当调用方法时,对象的属性有可能会改变
行为:事物的行为用可调用的成员方法来定义
意味着可以对对象完成哪些操作,或者可以对对象应用哪些方法
对象
是该类事物的具体体现
(例如:某个具体的学生)
类的定义
其实就是定义类的成员(成员变量,成员方法和构造器)
成员变量
表示事物的属性
位置:在类中,方法外
有默认值,不需要进行初始化也可以使用
例如
-
double salary; String name;
成员方法
表示事物可以发生的行为
例如:
-
public void raiseSalary(double byPercent) { double raise = salary * byPercent / 100; salary += raise; }
构造方法
给创建类的实例对象时用的,用于给对象的数据进行初始化
构造方法的定义
- 方法名与类名相同
- 无返回值类型,连void也没有
- 没有具体的返回值
类型
无参构造:若此时没有写构造方法,系统会自动提供无参构造方法,若已经自己写出了构造方法,系统将不再提供
-
public Order(){ }
有参构造:带参数进去的初始化
-
public Account(int number){…}
调用
构造器的调用其实就是利用它来创建类的实例
格式:用new关键字
-
类名 对象名 = new 构造方法名(…);
注意事项
- 系统本身会提供构造方法,但是是无参构造方法
- 如果已经写出了构造方法,系统将不再提供默认的无参构造方法,此时若还想使用无参的构造,必须自己提供(推荐无参和有参的都自己写)
- 构造方法是可以重载的(方法名相同,参数列表不同)
- 对于有参的构造方法,一创建对象就有相应的自己给出的初始值
封装
处理对象的一个重要概念
- 实现类的方法不能直接访问其他类的实例字段(对象中的数据)
- 客观世界里的成员变量都是隐藏在对象内部的,外界无法直接操作和修改
- 外界需要通过对象的方法与对象数据进行交互(间接)
原则
- 将不需要对外提供的内容隐藏起来
- 把属性隐藏,提供公共方法对其访问(间接的访问方式,就不需要直接来访问成员变量了)
方法
成员变量用private修饰,提供对应的getXxx()和setXxx()方法
好处
- 通过方法来控制成员变量的操作,提高了代码的安全性
- 把代码用方法进行封装,提高了代码的复用性
示例
封装前:外界可以直接通过 对象名.name直接访问
-
String name;
封装后:外界需要通过getName()和setName()方法来访问
-
public String getName() { return name; }
-
public void setName (String name) { this.name = name; }
类的示例
内容
- 成员变量(private)
- 构造方法:带参和无参
- 成员方法:getXxx()和setXxx()
- 给成员变量赋值的方式:
【1】无参构造+setXxx()
【2】带参构造
public class Student {
//成员变量
private String name;
private int age;
//构造方法
public Student() {}
public Student(String name,int age) {
this.name = name;
this.age = age;
}
//成员方法
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
}
类的使用
要想使用类的成员,必须先创建一个该类的对象,并指定其初始化状态,然后对对象调用方法(其实就是要创建类的实例对象)
步骤
- 使用构造器创建一个类的对象
类名 对象名 = new 类名(…);
- 右边:new 类名(…)的动作表明通过new操作符利用构造器创建了一个新的对象
- 左边:类名 对象名 = 的动作相当于把右边创建的对象地址存放在左边的对象变量中,这样利用对象变量就可以方便多次使用这个对象了
-
通过对象访问类的成员
使用成员变量:对象名.成员变量
给成员变量赋值的方法:
【1】直接访问成员变量赋值: staff.name = “Harry”;
【2】通过getXxx()和setXxx()方法来给成员变量赋值:setName(“Harry”);
【3】通过构造方法进行初始化值使用成员方法:对象名.成员方法
示例
/*
* 测试类
*/
public class StudentTest {
public static void main(String[] args) {
//无参构造方法+setXxx()
Student s = new Student();
s.setName("林青霞");
s.setAge(30);
System.out.println(s.getName()+"---"+s.getAge());
//带参构造方法
Student s2 = new Student("林青霞",30);
System.out.println(s2.getName()+"---"+s2.getAge());
}
}
关于类的内存图解
- 创建1个对象
- 创建2个对象
- 创建2个对象变量(指向同一个对象)
类的注意事项
- 定义类时候明确:成员变量和成员方法
- 类的使用可以通过一个测试类实现
- 最好用不同的类表示不同的概念
- 只要是某个类里面的对象,这个对象就一定符合类里面的属性和行为
成员变量和局部变量
区别
-
在类中的位置不同
成员变量:在类中,方法外
局部变量:在方法中或者方法声明上(形式参数) -
在内存中的位置不同
成员变量:在堆内存
局部变量:在栈内存 -
生命周期不同
成员变量:随着对象的创建而存在,随着对象的消失而消失
局部变量:随着方法的调用而存在,随着方法的调用完毕而消失 -
初始化值不同
成员变量:有默认值
局部变量:没有默认值,必须先定义,赋值,最后使用
就近原则
如果局部变量和成员变量名称一致,在方法中使用的时候采用的是就近原则
关键字和修饰符
this和super
this:指示编译器调用子类(本类)方法/构造器的特殊关键字
- 代表所在类的对象引用,指向现在所在的本类
- 方法被哪个对象调用,this就代表那个对象
super:指示编译器调用超类方法/构造器的特殊关键字
- 代表超类存储空间的标识,指向本类所引用的类
用法:
访问成员变量:
-
this.成员变量 super.成员变量
访问构造方法:
-
this(...) super(...)
访问成员方法:
-
this.成员方法() super.成员方法()
final
关键字final表示最终的意思,可以修饰类,方法和变量
修饰类:表明该类是最终类,不能被继承
修饰方法:表明该类是最终方法,不能被覆盖/重写
修饰变量:表明该变量为常量,不能被再次赋值
static
静态修饰符,修饰类中的成员(成员变量和成员方法)
作用
被类的所有对象共享
特点
- 在类中定义的
- 随着类的加载和加载,优先于对象的创建存在
- 用static修饰的成员可以通过类名直接访问:类名.成员
例如在Student类中,age被static修饰了,则:
-
Student.age = 18; // 被所有Student类的对象共享
访问特点
非静态的成员方法:
- 访问成员变量:静态or非静态
- 访问成员方法:静态or非静态
静态的成员方法:
- 访问成员变量:静态
- 访问成员方法:静态
注意事项
-
静态成员方法中不能出现this,super等关键字
原因:静态成员是随着类的加载而加载的,this,super这样的关键字是随着对象的创建而存在的,先进内存的,不能访问后进内存的 -
判断一个成员是否用static修饰,则看这个成员是否需要被类的所有对象共享
权限修饰符
类型
归纳
- 要想仅能在本类中访问使用private修饰
- 要想本包中的类都可以访问除了private修饰符,其它都可以
- 要想本包中的类与其他包中的子类可以访问使用protected修饰
- 要想所有包中的所有类都可以访问使用public修饰。
继承
Java程序设计中的一项核心技术
类和类之间关系的一种:继承
明显特征:“is-a”(是 关系)
基本思想
- 可以基于已有的类创建新的类,继承已存在的类就是复用(继承)这些类的方法,而且可以增加一些新的方法和字段,使新类能够适应新的情况。
- 多个类中存在相同的属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需在定义这些属性和行为,只要继承那个类即可
- 有了继承以后,我们定义一个类的时候,可以在一个已经存在的类的基础上,还可以定义自己的新成员
单独的这个类称为父类,基类或者叫超类,多个类可以称为子类或者派生类
概念
被继承的类:超类(superclass),基类(base class),父类(parent class)
继承的新类:子类(subclass),派生类(derived class),孩子类(child class)
定义
用extends关键字
格式
-
public class 子类名 extends 父类名{}
例子
-
public class Manager extends Employee { added methods and fields }
注意
- 通过扩展超类来定义子类的时候,只需要指出子类和超类的不同之处
- 设计类的时候,将最一般的方法放到超类中,而更特殊的方法放到子类中
- 这种将通用功能抽取到超类的做法在面向对象程序设计很普遍
好处和弊端
好处
- 提高了代码的复用性
- 提高了代码的维护性
- 让类与类之间产生了关系,是多态的前提
弊端:
- 让类与类之间产生了关系,也就让类的耦合性增强了。
继承特点
- Java只支持单继承,不支持多继承
- Java支持多级继承:
由一个公共超类派生出来到所有类的集合成为继承层次
从某个特定的类到其祖先的路径称为该类的继承链 - Object类是类层次结构的根类/超类,所有的类都间接或直接的继承自该类
成员特点
成员变量
成员变量名称不一样:直接区分
成员变量名称一样的情况:就近原则
在子类中访问变量:方法–子类–超类–报错
• 在方法的局部范围找,如果有就使用
• 在子类的成员范围找,如果有就使用
• 在超类的成员范围找,如果有就使用
• 如果还找不到 就报错
成员方法
子类中方法和超类中方法的声明不一样:直接区分
子类中方法和超类中方法的声明一样:执行的是子类中的方法 就近原则
通过子类对象调用方法:子类–超类–报错
- 在子类中找,有就使用
- 在超类中找,有就使用
- 如果没有就报错
构造方法
特点:
子类构造方法执行前都会先执行父类无参构造方法
原因:
因为子类继承父类,会继承父类的非私有成员。而子类在初始化的时候,可能会使用父类的数据,如果父类数据没有先初始化,子类就不能使用这些数据,所以,在子类初始化之前,一定要先完成父类数据的初始化。
注意:在子类的构造方法中,默认第一行有一条语句:super()
做法:
可以利用super语法调用父类的构造器:super(…);
注意事项:
- 使用super调用构造器的语句必须是子类构造器的第一条语句
- 若子类的构造器没有显示的调用超类的构造器,系统将自动的调用超类的无参构造器
- 即系统默认:子类构造器的第一条语句为:super();
- 若想要调用有参的构造方法,需要自己提供
示例:
public class Manager extends Employee {
//构造器
public Manager() {
//super();
}
public Manager(String name) {
//super();
super("Harry"); //调用有参的构造方法要自己提供
}
}
方法覆盖
概述
子类中出现了和超类中一摸一样的方法声明的情况,此时访问子类方法的时候,会按照就近原则使用子类的方法
应用
- 当子类需要超类的功能,而功能主体子类有自己特有的内容时,可以重写超类中的方法,这样既沿袭了超类的功能,又定义了子类特有的内容
- 用super关键词来访问超类的非私有方法
注解
表明该方法的重写超类的方法
-
@Override
注意事项
超类中私有方法和最终方法不能被重写
子类重写超类方法时,访问权限不能更低(建议访问权限一摸一样)
-
private--默认--protected--public
示例
定义一个Phone class是超类
public class Phone {
public void call(String name) {
System.out.println("给"+name+"打电话");
}
}
定义一个NewPhone class是子类,重写Phone中的call方法,这样既可以保留Phone中的call方法的功能,又能增加NewPhone自己特有的功能
public class NewPhone extends Phone {
@Override
public void call(String name) {
System.out.println("开启视频功能");
//System.out.println("给"+name+"打电话");
super.call(name); //直接通过super语法代表超类
}
}
测试类
public class PhoneTest {
public static void main(String[] args) {
Phone p = new Phone();
p.call("Harry");
System.out.println("-----------");
NewPhone np = new NewPhone();
np.call("Harry");
}
}
多态
概述
多态:
同一个对象,在不同时刻体现出来的不同状态
在Java程序设计语言中,对象变量是多态的,一个超类型的变量既可以引用一个超类型的对象,也可以引用子类的任何一个子类的对象
应用:
不能直接创建对象实例的时候,可以使用多态
好处:
提高了程序的扩展性
弊端:
不能访问子类特有功能
示例
让 class Manager继承了 class Employee:
Manager boss = new Manager();
Employee[] staff = new Employee[3];
staff[0] = boss;
此时boss和staff[0]同时引用Manager对象,实际上是一个Manager,但是编译器在这里只将staff[0]看成是一个Employee对象,意味着可以使用Employee对象的方法,但是不能使用子类Manager特有的方法
前提和体现
- 有继承关系
- 有方法重写
- 有超类引用指向子类对象
形式
接口多态(最常见)
抽象类多态(比较常见)
具体类多态(几乎不用)
特性
多态中成员的访问特点
(左边表示等号左边的对象变量的类型,右边表示等号右边的对象类型)
- 成员变量:编译看左边,执行看左边
- 成员方法:编译看左边,执行看右边
为什么成员变量和成员方法的访问不一样呢?
因为成员方法有重写,而变量没有
转型
对象引用的强制类型转换
类型:
向上转型:超类引用指向子类对象
向下转型:子类引用指向超类对象
使用原因
要在暂时忽略对象的实际类型之后使用对象的全部功能
语法:
用()将目标类名括起来
-
Manager boss = (Manager) staff[0];
注意:
转型之后,类型一定要匹配
示例:
定义一个超类Animal:
public class Animal {
public void eat() {
System.out.println("吃东西");
}
}
定义一个子类Cat继承Animal类:
public class Cat extends Animal {
public void eat() {
System.out.println("猫吃鱼");
}
public void playGame() {
System.out.println("猫捉迷藏");
}
}
多态的使用测试类:
public class PolymorphicDemo {
public static void main(String[] args) {
//多态
Animal a = new Cat(); //向上转型:超类指向子类
a.eat(); //此时编译器认为a是Animal对象
//a.playGame();
//多态的弊端:无法访问子类特有方法
Cat c = (Cat)a; //此时对象变量c和a同时指向了Cat对象
c.eat();
c.playGame();
}
}
多态内存图解
抽象类
概述
在Java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类。
定义
抽象类和抽象方法都必须用abstract修饰
格式:
定义抽象类:public abstract class 类名 {}
定义抽象方法:public abstract void eat();
(抽象方法没有方法体)
示例:
定义一个抽象类的Animal类
public abstract class Animal {
/*
public void eat() {
System.out.println("吃东西");
}
*/
//抽象方法
public abstract void eat();
}
public class AnimalDemo {
public static void main(String[] args) {
/*
Animal a = new Animal();
a.eat();
*/
//不能直接通过抽象类创建实例,因为是抽象的
}
}
抽象类特点
- 抽象类不一定有抽象方法,但是有抽象方法的类一定是抽象类
- 抽象类不能直接实例化:可以用多态的形式
- 抽象类的子类:
【1】要么重写/覆盖抽象类的抽象方法:意味着通过子类的实例对象来完成/实现某些具体的动作
【2】要么子类还是一个抽象类
示例:
定义一个Animal抽象类
//抽象类
public abstract class Animal {
//抽象方法
public abstract void eat();
//非抽象方法
public void sleep() {
System.out.println("睡觉");
}
}
定义一个抽象类的子类Cat类
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
抽象类测试:
public class AnimalDemo {
public static void main(String[] args) {
//创建对象
//Animal a = new Animal();
//按照多态的形式实例化抽象类
Animal a = new Cat();
a.eat(); //猫吃鱼
a.sleep(); //睡觉
}
}
抽象类的成员特点
成员变量
可以是变量
也可以是常量
构造方法
有构造方法,但是不能实例化
作用:用于子类访问超类数据的初始化
成员方法
可以有抽象方法:限定子类必须完成某些动作
也可以有非抽象方法:提高代码复用性
抽象类示例
定义一个抽象的老师类
public abstract class Teacher {
private String name;
private int age;
public Teacher() {}
public Teacher(String name,int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//抽象方法:子类必须重写抽象方法
public abstract void teach();
}
定义数学老师类,继承老师类
public class MathTeacher extends Teacher {
public MathTeacher() {}
public MathTeacher(String name,int age) {
super(name,age); //需要自己提供带参构造器
}
@Override
public void teach() {
System.out.println("数学老师教微积分");
}
}
测试使用数学老师的类
public class TeacherDemo {
public static void main(String[] args) {
//使用的是具体的类的对象
//MathTeacher
//多态形式的测试
Teacher t = new MathTeacher();
t.setName("Harry");
t.setAge(30);
System.out.println(t.getName()+"---"+t.getAge());
t.teach();
System.out.println("---------------------------");
t = new MathTeacher("林青霞", 30); //带参构造
System.out.println(t.getName()+"---"+t.getAge());
t.teach();
}
}
接口
概述
为了体现事物功能的扩展性,Java中就提供了接口来定义这些额外功能,并不给出具体实现
定义
用关键词interface实现
格式:public interface 接口名 {}
使用
其实就是用类实现接口
-
将类声明为实现给定的接口:implements
格式:public class 类名 implements 接口名 {} -
对接口的所有方法提供具体定义和实现
示例
定义了一个跳高的接口
public interface Jumpping {
//抽象方法
public abstract void jump();
}
定义Cat类实现这个接口
public class Cat implements Jumpping {
@Override
public void jump() {
System.out.println("猫可以跳高了");
}
}
给出这个接口的使用测试类
public class InterfaceDemo {
public static void main(String[] args) {
//Jumpping j = new Jumpping();
//接口多态的形式实例化
Jumpping j = new Cat();
j.jump();
}
}
接口特点
- 接口是抽象的,不能直接实例化:要用多态的形式–接口多态
- 接口的实现类:要么是抽象类,要么重写接口中的所有抽象方法
接口的成员特点
成员变量:
只能是常量
默认修饰符 public static final
构造方法:
没有,因为接口主要是扩展功能的,而没有具体存在
成员方法:
只能是抽象方法
默认修饰符 public abstract
注意
- 接口本身不能提供什么
- 接口绝不会有实例字段
- Java8之前,接口不会实现方法
- 提供实例字段和方法实现的任务应该由实现接口的那个类来完成,因此可以将接口看成是没有实例字段的抽象类
类和接口
关系
类与类:
继承关系,只能单继承,但是可以多层继承
类与接口:
实现关系,可以单实现,也可以多实现
还可以在继承一个类的同时实现多个接口
接口与接口:
继承关系,可以单继承,也可以多继承
区别
成员区别
抽象类:变量,常量;有抽象方法,抽象方法or非抽象方法
接口:常量;抽象方法
关系区别
类与类:继承,单继承
类与接口:实现,单实现,多实现
接口与接口:继承,单继承,多继承
设计理念区别
抽象类:被继承体现的是:”is a”的关系(共性功能)
接口:被实现体现的是:”like a”的关系(扩展功能)
包
概述:
其实就是文件夹
作用:
对类进行分类管理
定义:
格式:package 包名;
多级包用(.)分开即可
使用:
不同包下的类之间的访问,我们发现,每次使用不同包下的类的时候,都需要加包的全路径。比较麻烦。这个时候,java就提供了导包的功能
导包格式:import 包名;
注意事项:
- package语句必须是程序的第一条可执行的代码
- package语句在一个java文件中只能有一个