目录
前言
上回说到Java的基本数据类型、操作符、权限修饰符,本次我们来一起学习一下Java最耳熟能详的面向对象相关概念。各位小伙伴,总结不易,感觉对你有帮助的麻烦来波点赞关注!!!
一、什么是面向对象?
对象的特点:
- 对象具有属性和行为。
- 对象具有变化的状态。
- 对象具有唯一性。
- 对象都是某个类别的实例。
- 一切皆为对象,真实世界中的所有事物都可以视为对象。
面向对象开发模式更有利于人们开拓思维,在具体的开发过程中便于程序的划分,方便程序员分工合作,提高开发效率。面向对象程序设计的优点:
- 可重用性:代码重复使用,减少代码量,提高开发效率。
- 可扩展性:指新的功能可以很容易地加入到系统中来,便于软件的修改。
- 可管理性:能够将功能与数据结合,方便管理。
二、面向对象的三大特征
1.封装
基本概念:是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法
优点:
-
1. 良好的封装能够减少耦合。
-
2. 类内部的结构可以自由修改。
-
3. 可以对成员变量进行更精确的控制。
-
4. 隐藏信息,实现细节。
我们创建对象的时候可以通过修改属性的可见性(一般用private修饰)来限制对对象属性的直接访问,然后通过对每个属性值提供对外的公共方法,通过公共方法对私有属性进行访问
代码如下(示例):
public class User {
private String name;
private String age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
2.继承
基本概念:子类继承父类的属性和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
优点:避免重复,易于维护,易于理解
代码如下(示例):
创建父类User类,定义公共的属性和方法
public class User {
//属性
private String name;
private String age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
//行为
void say(){
System.out.println("我是"+name+","+"今年"+age+"了");
}
}
定义子类vip类,用extend关键字继承User父类,并创建它独有的属性和方法。
public class Vip extends User{
//属性
private String level;
public String getLevel() {
return level;
}
public void setLevel(String level) {
this.level = level;
}
//行为
void level(){
System.out.println("我的vip等级是"+level);
}
}
创建测试类userTest,创建vip对象发现它同时又user和vip的属性和行为
public class UserTest {
public static void main(String[] args) {
Vip vip = new Vip();
//vip这个类因为继承了user这个类之后有了user的属性及方法
vip.setName("张三");
vip.setAge("22");
vip.setLevel("3");
vip.say();
vip.level();
}
}
3.多态
基本概念:指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。(发送消息就是函数调用)
优点:解除类型之间的耦合关系
多态的表现形式有重载、重写、抽象类、接口,下面一一介绍他们。
1.重载
基本概念:重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
规则:
- 被重载的方法必须改变参数列表(参数个数或类型不一样);
- 被重载的方法可以改变返回类型;
- 被重载的方法可以改变访问修饰符;
- 被重载的方法可以声明新的或更广的检查异常;
- 方法能够在同一个类中或者在一个子类中被重载。
- 无法以返回值类型作为重载函数的区分标准。
最常见的就是构造方法,下面代码中三个不同参数但是相同方法名的构造方法就是重载的实现。
代码如下(示例):
public class User {
//属性
private String name;
private String age;
//无参构造
public User() {
}
//有参构造
public User(String name, String age) {
this.name = name;
this.age = age;
}
//有参构造
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
//行为
void say(){
System.out.println("我是"+name+","+"今年"+age+"了");
}
}
2.重写
基本概念:重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。
规则:
-
参数列表与被重写方法的参数列表必须完全相同。
-
返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。
-
访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。
-
父类的成员方法只能被它的子类重写。
-
声明为 final 的方法不能被重写。
-
声明为 static 的方法不能被重写,但是能够被再次声明。
-
子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
-
子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
-
重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
-
构造方法不能被重写。
-
如果不能继承一个类,则不能重写该类的方法。
还是用user的例子,我们在父类user中定义一个identity()方法
代码如下(示例):
public class User {
//属性
private String name;
private String age;
public User() {
}
public User(String name, String age) {
this.name = name;
this.age = age;
}
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
//行为
void say(){
System.out.println("我是"+name+","+"今年"+age+"了");
}
void identity(){
System.out.println("我是用户!");
}
}
然后我们在子类vip中继承一下这个方法
public class Vip extends User{
//属性
private String level;
public Vip(String name) {
super(name);
}
public String getLevel() {
return level;
}
public void setLevel(String level) {
this.level = level;
}
//行为
void level(){
System.out.println("我的vip等级是"+level);
}
void identity(){
System.out.println("我是VIP!");
}
}
最后在测试类我们分别创建子类对象、父类对象、父类引用指向子类对象,看看他们分别对应的identity()方法输出结果有什么不同
public class UserTest {
public static void main(String[] args) {
Vip vip = new Vip("张三");
//vip这个类因为继承了user这个类之后有了user的属性及方法
// vip.setName("张三");
vip.setAge("22");
vip.setLevel("3");
vip.say();
vip.level();
vip.identity();
//父类引用指向子类对象
User user = new Vip("李四");
user.setAge("23");
user.say();
user.identity();
//父类对象
User user1 = new User("王五");
user1.setAge("23");
user1.say();
user1.identity();
}
}
运行结果很明显,除了父类本身创建的对象之外,通过子类创建的都会重写父类的identity()方法。
3.抽象类
基本概念:抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样
规则:
-
1. 抽象类不能被实例化(初学者很容易犯的错),如果被实例化,就会报错,编译无法通过。只有抽象类的非抽象子类可以创建对象。
-
2. 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
-
3. 抽象类中的抽象方法只是声明,不包含方法体,就是不给出方法的具体实现也就是方法的具体功能。
-
4. 构造方法,类方法(用 static 修饰的方法)不能声明为抽象方法。
-
5. 抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。
我们创建一个抽象类,抽象类用abstract关键字修饰,并在该抽象类定义属性、行为、构造方法、还有抽象方法(抽象方法只能在抽象类中定义)
代码如下(示例):
public abstract class People {
//属性
private String name;
private String age;
private String sex;
//构造方法
public People(String name, String age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
//行为
void say(){
System.out.println("我是"+name+",一个"+age+"岁的"+sex+"孩。");
}
//抽象方法
public abstract void love();
}
因为抽象方法没有办法实例化,只能通过可以被实例化的子类实现,所以我们创建一个对象继承这个抽象类,并且重写一下抽象方法
public class Teacher extends People{
public Teacher(String name, String age, String sex) {
super(name, age, sex);
}
//重写抽象方法
public void love() {
System.out.println("老师爱教书!");
}
}
接下来我们创建对象测试一下
public class AbstractDemo {
public static void main(String[] args) {
Teacher t = new Teacher("小红","20","女");
t.say();
t.love();
}
}
4.接口
基本概念:接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。
规则:
- 接口不能用于实例化对象。
- 接口没有构造方法。
- 接口中所有的方法必须是抽象方法,Java 8 之后 接口中可以使用 default 关键字修饰的非抽象方法。
- 接口不能包含成员变量,除了 static 和 final 变量。
- 接口不是被类继承了,而是要被类实现。
- 接口支持多继承。
先使用interface关键字创建一个pet接口,定义两个方法eat()和play()
public interface Pet {
void play();
void eat();
}
创建一个类来实现这个接口,重写接口中的方法就可以通过实例化创建的对象来调用接口中的方法
public class cat implements Pet {
public void play() {
System.out.println("猫抓老鼠");
}
public void eat() {
System.out.println("猫吃小鱼干");
}
public static void main(String[] args) {
cat cat = new cat();
cat.eat();
cat.play();
}
}
运行结果:
总结
Java面向对象相对来说这块儿小知识点复杂且容易搞混,需要多加联系,先理解后动手。鉴于这块儿有很多容易混淆的知识点,下期我专门做一期他们之间的对比来帮助大家理解。整理不易,希望感觉对你有帮助的麻烦点赞关注一下,发现问题也欢迎及时指正沟通,相互交流进步!!!