6.面向对象的三大基本特征

本文详细解释了Java中的封装概念及其优势,包括如何通过封装简化代码管理和提高代码复用性。同时,文章阐述了多态的概念,展示如何通过多态特性简化代码结构,提高程序的灵活性和可维护性。

封装

有些人可能学Java有半年了,还是搞不懂封装是什么东西。其实我们每天都在用封装。那么封装是什么呢?封装简单来说就是包装。比如说:我们把一堆数据放在一个类里面,并且加上get和set方法,这个就是JavaBean封装。再比如:我们把一些通用的代码放在一个方法里面,以后需要的时候直接调用方法就行了,这就是代码封装。

那么,为什么要用封装呢?封装有什么好处呢?

比如:我们需要一个学生的信息:姓名,年龄。并且在控制台显示出来,我们可以这样写:

public static void main(String[] args){

String name;

int age;

System.out.println("这个学生的名字是:"+name+",年龄是:"+age);

}

那么我想再定义一个学生信息,并且显示呢?我还需要重复这样的代码:重新定义两个变量并赋值。那么再想想,我需要十个学生的信息并且显示呢?还有,实际情况中,一个学生的信息不可能就这么几个变量,可能有十几个呢?难道我每次需要显示学生信息,都要定义这么多变量吗?想想吧,这真是太可怕了。

属性的封装

但是,现在我们可以用封装来实现,怎么实现呢?我们可以把所有的属性放在一个类中,这样,我需要显示学生信息时,只需要实例化一个类就行了。比如:

public class Student{

String name;

int age;

}

我们需要一个学生信息时可以这样写:

Student student=new Student();

student.name="zhangsan";

student.age=12;

System.out.println("name:"+name+",age="+age);

有人可能会说:我怎么感觉除了代码量变大之后没有什么好处啊?是的,单单看这段代码,确实是如此,但是假如我们需要显示一个班级56个学生的信息,而这个班级的所有学生信息的年龄都是相同的,那么我们可以这样定义Student:

public class Student{

String name;

int age=12;

}

那么,实例化一个学生的信息时,我们只需要定义学生姓名就可以了,年龄就默认赋值了,大家现在可以再次对比一个封装和不封装的代码量,你就可以知道哪种更好了。

毫无疑问,从长远角度看以及从更好的适用性来看,通过将数据封装成对象,是很有必要的。

而且,假如你需要显示学生的信息,要显示学生年龄而只知道学生生日时,你需要这样写:

String birthday="2002-01-03";

//截取生日的前四位,获取出生的年份

....

//得到当前的系统时间,从而知道现在是哪一年

....

//通过减法得到年龄

....

//最后定义年龄变量并将计算结果赋值

....

好的,代码就不写了,懂什么意思就行了。那么,大家看这段伪代码,我第一行定义了生日变量,第五行定义了年龄变量,那么请问,假如这个程序有100行,你觉得可能是不是在第80行还可能又出现一个变量?那么,假如你过了很久又看这个程序或者别人看这个程序,他能一眼看出你总共定义了多少跟学生有关的变量吗?毫无疑问,很不方便的,但是,你假如通过封装之后呢?你需要看一下Student类中有多少属性,就马上知道了一个学生有多少变量了。

现在,再看学生Student这个类,假如我在main方法中给一个学生的age属性赋值-1,你说程序会不会出错?当然不会,你又没有规定年龄不能为负数。那么怎么办呢?两步:

一:将属性私有化,这样别人就不能直接访问变量了。

二:将访问变量的方式改为通过方法来访问,可以在方法中增加限制条件

结果如下:

class Student{

private String name;

private int age;

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public int getAge() {

return age;

}

public void setAge(int age) {

if(age>0){

this.age = age;

}

}

}

那么请问大家,这样是不是很好呢?要想改年龄赋值,必须调用setAge()方法,那么年龄必须大于0。

代码的封装

代码的封装我就不需要讲了,就是把一段代码封装成一个方法,方便以后的调用。

继承

继承就是子承父业。就是子类可以继承父类的所有属性和方法。有什么用呢?

比如我有一个Father类,姓是"赵",还有一个Son类,姓当然也是"赵"了,那么现在我们按一般的思路,就是建两个类,都有姓这个属性,值都是赵。但是呢?这样我感觉很不爽啊,假如这两个类有十个属性都是一样的,我还需要两个类都这样写吗?太糟糕了。

因此Java产生了继承的概念:

我们可以定义一个Father类,而子类Son继承Father类,这样,姓这个属性就继承过来了,不需要再写一次。

public class Father{

String name="赵";

}

public class Son extends Father{

   

}

虽然Son里面空空的,但是你可以调用Son的name属性。这就是封装的迷人地方。

你可能会说,我可能想改姓呢,怎么办呢?只需要再Son中再定义一次name就好了。

public class Son extends Father{

String name="刘";

}

继承最大的优点就是我们可以重用一个以前写好的类,继续使用原来类中的属性和方法,我们甚至可以修改原来类中某些属性或方法,以符合当前类的需要。

当然,缺点就是假如因为某些原因,你忘了某个类是被继承了,而你又改了这个类的一个属性,那么,它的子类的这个属性都将会改变。比如Father类我name改为"李",而你又不知道这个类被继承了(单看Father类你真不知道这个类被继承了),那么你的所有子类,比如Son类的name也会变成"李"。那么,这个后果是很严重的,因为你可能用到的Sonname应该是赵,不能是李。

多态

多态就是多姿多态。一朵花可以有多种形态:兰花,荷花,水仙花等等。这个世界就是多姿多态的,比如:铅笔有白铅笔,蓝铅笔,花铅笔。鸟有鸡,鸭,鹅等等。那么这些对象之间都有一定 的共性,我们可以运用刚学过的继承概念来写几个类出来。

比如:圆形,方形,三角形都是形状的子类。

//Shape是形状的意思

class Shape{

//形状都有长度

int length;

//形状都有面积

int area;

public int getLength() {

return length;

}

public void setLength(int length) {

this.length = length;

}

public int getArea() {

return area;

}

public void setArea(int area) {

this.area = area;

}

}

class Circle extends Shape{

public int getLength() {

System.out.println("用圆形公式求圆形的长度");

return length;

}

public int getArea() {

System.out.println("用圆形公式求圆形的面积");

return area;

}

}

class Square extends Shape{

public int getLength() {

System.out.println("用方形公式求方形的长度");

return length;

}

public int getArea() {

System.out.println("用方形公式求方形的面积");

return area;

}

}

现在我需要实例化一个圆形和方形,并且求出它们的长度,怎么写呢?

public static void main(String[] args){

Circle circle=new Circle();

Square square=new Square();

circle.getLength();

square.getLength();

}

有人可能会问了,我们想要知道多态的概念,可你讲了半天,怎么都是继承啊!别急,多态来了。

在这里,Java出现了一种新的语法:子类的实例化对象可以被父类的引用表示。代码表示就是:

Shape shape1=new Circle();

为什么这样写呢?好难理解啊。呵呵,这样理解:一个圆形是一个形状对吧?那么,我可以在纸上画一个"圆形",并且说,这是一个"形状"。好,我重复一遍,我指着一个圆说这是一个形状。没问题吧?圆是实际存在的吧?所以我们实例化一下:

new Circle();

我说这是一个形状,这个形状我给它起名字叫shape1:

Shape shape1;

将两边连起来就是:

Shape shape1=new Circle();

现在大家理解了吧?

那么这样有什么好处呢?好处嘛,就是:

Shape shape1=new Circle();

shape1.getLength();

这段代码会得到圆的长度,毕竟我指着的东西是一个圆。

我也可以在这段代码后面加一段:

shape1=new Square();

shape1.getLength();

这段代码又会得到方形的长度,是不是很有趣?

但是它真正的好处并不是这样的,而是:

public staitc void getLenth(Shape shape){

shape.getLength();

}

public static void main(String[] args){

getLength(new Circle());

getLength(new Square());

}

看见了吗?我需要传进去一个Shape子类的实现,就可以调用这个子类的相应的方法,而代码则没有变化,一切都是Java自动帮你完成。

我们甚至可以把Circle这个类以及它的包路径存在一个文件中,比如XML文件中,实例化对象时从文件中取得Circle的信息,通过反射实例化,那么这样就彻底的脱离了具体的实现:

public static void main(String[] args){

//通过文件得到Circle的包路径"com.test.Circle"

Stirng circleStr=getPath();

//通过反射实例化

Shape shape1=Class.forName(circleStr).getInstance();

getLength(shape1);

}

那么这有什么用呢?

1.隐藏了具体的实现代码,你知道调用了getLength方法,但你如果不看文件的内容,你就不知道调用的方法具体内容是什么,因为程序中根本没有体现。

2.傻瓜式代码,我只需要改改文件中的内容,就可以瞬间改变程序的运行结果,即使这个人一点程序基础也没有也可以做到。

3.也就是我们平时开发中为什么经常用到多态的原因:我们需要继续原来的项目运行而又因为客户的需要而改代码时,我们不需要要求客户停止现在的项目,说我们需要改改,等几个月你再运行吧。No,这是在开玩笑,我们可以继承指定的接口,而重写实现类,到时在凌晨3点时,停止系统几分钟,改一下配置文件,瞬间项目就改变了。

<think>首先,用户的问题是:“我想了解Java基础中面向对象三大特征之一——继承的概念和用法请问Java面向对象继承详解”。根据系统指令,我需要用中文回答,结构清晰,逐步解决用户的问题。用户还提到了参考站内引用:[^1]、[^2]、[^3]、[^4]。这些引用是关于Java面向对象的:-[^1]:提供了Java面向对象编程的基本概念和扩展知识。-[^2]:关于Java接口的特性。-[^3]:展示继承中super关键字的使用,在子类构造方法中调用父类构造。-[^4]:关于继承和多态,提到了setter和getter方法以及继承的内容。用户的问题特别针对继承,所以我需要重点引用[^3]和[^4],因为它们直接涉及继承。用户上一次的问题是关于Java基础中面向对象三大特征之一——继承,而我需要确保我的回答基于这些引用。系统指令要求:-所有行内数学表达式用$...$格式(但Java中没有数学表达式,所以不需要)。-独立公式用$$...$$(同样不适用)。-LaTeX语法正确(无)。-使用中文回答。-生成相关问题(指令6:生成相关问题)。-回答中引用的段落末尾自然地添加引用标识,例如:[^1]。此外,指令说:“回答中引用的段落末尾自然地添加引用标识”,意思是当我在回答中引用这些内容时,要添加[^1]等标签。最后,指令6是“生成相关问题”,在系统级指令中提到了生成相关问题。在用户可见层,它说“§§相关问题§§”,但在示例中是生成相关问题。现在,构建我的回答:-**结构清晰**:从基本概念开始,解释继承,然后到用法,一步步讲解。-**使用引用**:-引用[^1]:可以用来引入面向对象编程的基本概念。-引用[^2]:讨论接口,可能对比继承(例如,接口vs.抽象类),但不是核心,所以少用。-引用[^3]:直接显示super关键字在继承中的使用。-引用[^4]:提到setter和getter方法,以及继承的内容,适合讨论访问成员变量。-因为用户特别问了继承,我应该强调[^3]和[^4]。关键点:1.**概念**:继承是什么?它是OOP三大特征之一(封装、继承、多态),允许一个类继承另一个类的属性和方法。[^1]提到“Java面向对象编程详解”,可以引用。2.**语法**:如何在Java中实现继承?使用关键字"extends"。例如,classStudentextendsPerson。[^3]和[^4]有例子。3.**用法**:-重用代码(父类代码被子类重用)。-方法覆盖(override)。-访问控制:private成员不能被继承,protected可以。-使用super关键字调用父类的方法和构造。-具体例子:[^3]显示在子类构造中使用super()。-Getter和setter:[^4]提到private成员变量,通过setter和getter访问。4.**限制**:-Java不支持多继承(一个类只能继承一个父类)。-Final类不能被继承。-抽象类和接口的区别([^2]提到接口更抽象)。5.**优势和应用**:减少代码冗余,实现分层结构。在回答中,自然地添加引用:-例如,说到概念时,加[^1]。-说到super时,加[^3]。-说到setter/getter时,加[^4]。生成相关问题:在最后部分,添加"§§相关问题§§",并列出几个相关问题,如系统示例。完整回答结构:-开头:介绍继承的概念。-然后:详细解释。-代码示例:嵌入Java代码示例。-结尾:总结。-最后:生成相关问题。</think> 以下是针对您的查询——Java基础中面向对象三大特征之一“继承”的详细讲解。我会从基本概念入手,逐步解释其用法、语法、常见应用和注意事项,确保结构清晰、易于理解。内容基于您提供的站内引用(如[^1]、[^3]和[^4]),并结合Java核心知识点进行综合说明。引用会在相关内容末尾自然添加标识。 ### 1. **继承的概念** 继承是Java面向对象编程(OOP)的三大核心特征之一(包括封装、继承和多态),它允许一个类(称为子类或派生类)继承另一个类(称为父类或基类)的属性和方法。这样做的目的是实现代码重用和层次结构组织:子类可以复用父类的非私有成员(如变量和方法),同时可以定义新成员或覆盖(override)父类的行为,从而创建更具体的派生类[^1]。 在Java中,继承使用关键字`extends`来实现。例如: ```java // 父类 Person public class Person { private int age; // 私有成员变量,不能被直接继承 private String name; // 父类构造方法 public Person(String name, int age) { this.name = name; this.age = age; } // 公共方法,可被子类继承 public void displayInfo() { System.out.println("Name: " + name + ", Age: " + age); } // Getter和Setter方法(用于访问私有变量) public int getAge() { return this.age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } } // 子类 Student 继承 Person public class Student extends Person { private String studentId; // 子类特有成员变量 // 子类构造方法,使用super调用父类构造 public Student(String name, int age, String studentId) { super(name, age); // 调用父类的带参构造,确保父类变量初始化[^3] this.studentId = studentId; } // 覆盖父类方法(多态的基础) @Override public void displayInfo() { super.displayInfo(); // 调用父类的displayInfo方法 System.out.println("Student ID: " + studentId); } // 子类特有方法 public void study() { System.out.println("Studying now..."); } } ``` 在这个例子中: - `Student`继承了`Person`的所有非私有成员(如`displayInfo`方法和getter/setter)。 - 子类通过`super`关键字调用父类构造(如`super(name, age)`)和覆盖方法(如`displayInfo`)。 - 父类的私有变量(如`age`)不能被直接访问,需要通过getter/setter(如`getAge()`)来操作,体现封装原则[^4]。 ### 2. **继承的用法详解** 继承在Java中的使用涉及以下几个方面,我将其分步说明: #### (a) **基本语法和使用** - **声明继承**:使用`extends`关键字定义子类与父类的关系。 ```java public class SubClassName extends SuperClassName { // 子类定义 } ``` 例如,`Student extends Person`表示`Student`是`Person`的子类。 - **访问成员**: - 子类可以直接访问父类的`public`和`protected`成员(方法或变量)。 - 私有成员(`private`变量如`age`)不能直接访问,必须通过父类提供的getter/setter方法(如`getAge()`和`setAge()`)来操作,这保证数据的安全性[^4]。 - 如果父类没有默认构造方法,子类必须显式调用父类的构造方法使用`super(...)`[^3]。 #### (b) **super关键字的作用** `super`是继承中的关键元素,用于: - **调用父类构造**:在子类构造方法中,`super()`必须是第一行代码(如果没有默认构造,必须显式调用)。 ```java public Student(String name, int age, String studentId) { super(name, age); // 调用Person的带参构造[^3] this.studentId = studentId; } ``` - **调用父类方法**:在子类覆盖方法时,使用`super.methodName()`可以调用父类的原始实现。 ```java @Override public void displayInfo() { super.displayInfo(); // 先执行父类的displayInfo方法 // 添加子类特定逻辑 } ``` #### (c) **方法覆盖(Override)** 子类可以覆盖父类的非`final`方法来提供特定实现: - **规则**: - 方法名、参数列表和返回类型必须相同。 - 访问权限不能比父类更严格(例如,父类是`public`,子类不能是`private`)。 - 不能在子类中覆盖`private`或`static`方法。 - **示例**: ```java // 在Student类中覆盖displayInfo方法 @Override public void displayInfo() { // 调用父类方法后添加额外输出 super.displayInfo(); System.out.println("Student-specific info."); } ``` 这样,当调用`student.displayInfo()`时,会执行子类的实现。 #### (d) **类型转换和多态** 继承支持多态(另一个OOP特征),即父类引用可以指向子类对象: - **向上转型**:自动类型转换,父类变量引用子类对象。 ```java Person person = new Student("Alice", 20, "S12345"); // 合法,子类对象赋值给父类变量 person.displayInfo(); // 调用的是子类的覆盖方法(如果有) ``` - **向下转型**:需要显式强制类型转换,但有风险(可能`ClassCastException`),应使用`instanceof`检查。 ```java if (person instanceof Student) { Student student = (Student) person; // 安全向下转型 student.study(); } ``` #### (e) **继承的限制和注意事项** - **单一继承**:Java不支持多继承(一个类只能继承一个父类),但可以通过接口(`interface`)实现类似效果(引用[^2]提到接口是更抽象的机制)。 - **类访问控制**: - `final`类不能被继承(如`public final class FinalClass`)。 - 父类的`private`成员对子类不可见。 - **构造方法**:子类构造默认调用父类的无参构造(`super()`),如果父类没有无参构造,必须显式调用`super(parameters)`[^3]。 - **设计原则**:过度使用继承可能导致耦合度高。优先组合(composition)而不是继承("is-a"关系才用继承),如设计模式中的策略模式。 ### 3. **继承的优势和常见应用场景** - **优势**: - **代码重用**:复用父类逻辑,减少冗余代码。 - **可扩展性**:子类添加新功能而不修改父类。 - **结构清晰**:创建类层次(如`Animal`父类,`Cat`和`Dog`子类)。 - **多态支持**:实现运行时动态绑定,便于系统扩展[^4]。 - **应用场景**: - GUI框架:如JavaFX或Swing组件继承。 - 数据库访问:DAO模式中基础DAO类被特化DAO继承。 - 业务系统:用户角色管理(如`User`父类,`Admin`和`Guest`子类)。 - 引用[^4]中的内容,继承常用于基于实体的系统(如电商中的`Product`基类,`Book`或`Electronic`子类),通过setter/getter管理变量。 ### 4. **总结** 继承是Java OOP的核心,通过`extends`实现层次关系,super用于父类成员访问,覆盖支持行为定制。它强调“is-a”关系(如Student is a Person),在代码复用和多态中作用重大。实践中,应结合封装(使用getter/setter保护私有变量[^4])和设计原则来避免缺陷。如果您有具体代码示例或更深问题,我可以进一步解析!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值