书读的越多而不加思考,你就会觉得你知道得很多;而当你读书而思考得越多的时候,你就会越清楚地看到,你知道得很少。——伏尔泰
一、面向对象和面向过程
面向对象:
- 把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为
面向过程:
- 分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现, 使用的时候一个一个依次调用就可以了
光看定义实在难理解,下面这个网上的解析很不错
举个例子:五子棋程序
面向过程:
先分析问题的步骤:
- 开始游戏,
- 黑子先走,
- 绘制画面,
- 判断输赢,
- 轮到白子,
- 绘制画面,
- 判断输赢,
- 返回步骤2,
- 输出最后结果。
把上面每个步骤用不同的方法来实现。
面向对象:
功能来划分问题,先构建类和对象:整个五子棋可以分为
- 黑白双方,这两方的行为是一模一样的,
- 棋盘系统,负责绘制画面,
- 规则系统,负责判定诸如犯规、输赢等。
第一类对象(玩家对象)负责接受用户输入,并告知第二类对象(棋盘对象)棋子布局的变化,棋盘对象接收到了棋子的i变化就要负责在屏幕上面显示出这种变化,同时利用第三类对象(规则系统)来对棋局进行判定。
面向对象设计功能上的统一保证了可扩展性。比如我要加入悔棋的功能,如果要改动面向过程的设计,那么从输入到判断到显示这一连串的步骤都要改动,甚至步骤之间的循序都要进行大规模调整。如果是面向对象的话,只用改动棋盘对象就行了,棋盘系统保存了黑白双方的棋谱,简单回溯就可以了,而显示和规则判断则不用顾及,同时整个对对象功能的调用顺序都没有变化,改动只是局部的。
那么哪种好呢?有网友总结的很生动:
面向过程:蛋炒饭,
- 鸡蛋和米饭混合炒,分布均匀,每一粒米饭上都散布着金黄色,真香!
- 但是我不吃鸡蛋,只能倒掉了,太浪费了。
面向对象:盖浇饭,
- 底下一盘饭,饭上盖上菜。你喜欢吃什么菜,就放什么菜,杜绝浪费!
- 但是米饭和菜入味没那么好,不太香啊。
总结:各有千秋,对于家庭,面对过程就很适合,人少要求少,出菜也快。
对于饭馆,面对对象更胜一筹,虽然出菜慢,但是饭菜种类多,也浪费少。
面向过程:优点: 性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;
面向对象:优点: 易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统 更加灵活、更加易于维护
二、4大特性:抽象,封装,继承,多态
1.抽象:
定义: 抽象就是将一类实体的共同特性抽象出来,封装在一个新的概念(类) 中,所以抽象是面向对象语言的基础
举例: 猫、狗都可以抽象为动物类
状态(属性):体重,身高,颜色等
行为(方法):吃,睡,叫,抓等
实践代码,搓搓小手动起来(=!=):
public class Animal {
//动物类属性
String name;
String colour;
double weight;
double height;
//动物类方法
public void eat(){
System.out.println("吃");
}
public void sleep(){
//name = "花花";
System.out.println("睡觉");
}
public void shout(){
System.out.println("喊叫");
}
}
public class AnimalTest {
public static void main(String[] args) {
Animal cat = new Animal();
cat.name = "五月";
cat.colour = "白色";
cat.weight = 5.6;
cat.height = 0.36;
System.out.println(
"猫咪" +cat.name+":毛色"+cat.colour+"|身高:"+cat.height+"米"+"|体重:"+cat.height+"千克");
Animal dog = new Animal();
dog.name = "啸天";
dog.colour = "黄色";
dog.weight = 20;
dog.height = 0.85;
System.out.println(
"狗狗" +dog.name+":毛色"+dog.colour+"|身高:"+dog.height+"米"+"|体重:"+dog.height+"千克");
cat.eat();
cat.shout();
cat.sleep();
dog.eat();
dog.shout();
dog.sleep();
}
输出结果:
2.封装
定义: 把描述一个对象的属性和行为的代码封装在一个”模块“中,也就是类中。
即将对象封装成一个高度自治和相对封闭的个体,对象状态(属性)由这个对象 自己的行为(方法 )来读取和改变
目的: 实现软件内部的”高内聚、低耦合“,防止程序相互依赖而带来的变动影响。
举例: 学生要画个圆,这涉及了2个对象:学生和圆。那么画圆的方法分配给哪个对象呢?在类中把圆心和半径定义成了私有变量,画圆的方法必须要分配给圆,才能访问到 圆心和半径这2个属性。学生只是调用这个画圆方法,给圆发个消息:画圆。
实践代码,搓搓小手动起来(=!=):
public class Circle {
//圆心
private double x;
private double y;
//半径
private double radius;
public void huaYuan(double x,double y ,double radius){
System.out.println("圆画好了,圆点在:(" +x +"," +y+"),半径是"+radius +"cm");
}
}
public static void main(String[] args) {
Circle circle = new Circle(); //创建了变量名为circle的类Circle的对象
circle.huaYuan(1.2,4.4,7.867);
}
输出结果图片:
3.继承
定义: 从已存在的类派生出新的类,前者称为父类,后者叫做子类。
子类可以获取到父类的属性和方法
举例: 金毛狗类属于狗类,狗类属于哺乳动物类,哺乳动物类属于动物类,动物类属于生物类
(Java中的类实现单继承 ,不支持多重继承)
规则:
-
子类可以继承父类的:
public、protected 、默认权限(此时需要子类父类同包)的成员。
不能继承:private 权限修饰的不能被继承使用(属性和方法)
子类不能继承父类中构造方法
创建子类:class SubClass extends SuperClass {……} -
继承属性时:
子类可以直接使用,还可以对其修改,也就是变量的隐藏–>在子类中调用或者创建子类对象调用,
如果子类修改了继承自父类的属性,其属性值为修改后的值,反之则为父类的属性值 -
继承方法时:
方法的重写:方法名相同,方法的参数列表相同,方法的返回值类型相同,权限要大于等于父类的权限,
子类抛出的异常只能为父类异常的子类 或者本身 或者不抛异常
在子类中调用或者子类对象调用时,如果子类重写方法,调用的是重写后的方法,反之则调用父类的方法
final ,static修饰的方法都不能被重写 -
super
用来引用当前对象的父类对象, super来实现对父类成员的访问
用来调用父类的构造函数super( [参数列表] )必须放在子类构造方法的句首。
如果子类没有明确的调用父类的构造函数,编译器会自动的调用super()语句,它调用父类的默认构造函数
特性:
-
可传递可扩展性:
若类C继承类B,类B继承类A,则类C既有从类B那里继承下来的属性与方法,
也有从类A那里继承下来的属性与方法,还可以有自己新定义的属性和方法 -
可复用性:
若类B继承类A,那么建立类B时只需要再描述与基类(类A)不同的少量特征(数据成员和成员方法)即可,
这种做法能减小代码和数据的冗余度,大大增加程序的重用性 -
可维护性:
继承通过增强一致性来减少模块间的接口和界面,大大增加了程序的易维护性
实践代码,搓搓小手动起来(=!=):
public class Animal {
//动物类属性
String name= "大大";
String colour;
double weight;
double height;
//动物类方法
public void eat(){
System.out.println("吃");
}
public void sleep(){
//name = "花花";
System.out.println("睡觉");
}
public void shout(){
name = "花花";
System.out.println("喊叫");
}
}
//类Dog继承了类Animal
public class Dog extends Animal{
public static void main(String[] args) {
Dog dog = new Dog();
dog.eat();//调用父类Animal的eat()方法
dog.sleep();//调用重写后的sleep()方法
dog.sleep(4);//调用重载后的sleep()方法
}
//重写sleep方法
public void sleep(){
super.sleep();//调用父类Animal的sleep()方法
System.out.println(super.name);//调用父类Animal的sleep()方法中name的值
System.out.println("重写了睡觉");
}
//重载sleep
public void sleep(int i){
System.out.println("睡了"+i+"个小时");
}
//重写shout
public void shout(){
System.out.println("汪汪汪");
}
}
输出结果图片:
4.多态
定义: 多态是指不同的类对象收到同一消息可以产生完全不同的响应
(实现一个功能,切换不同的对象,来实现不同的效果)
父类类型的变量指向子类创建的对象,使用该变量调用父类中一个被子类重写的方法, 则父类中的方法呈现出不同的行为特征。
三种实现方式:
- 普通类(下面代码会演示)
父类 变量名 = new 子类();
class Fu {}
class Zi extends Fu {}
//类的多态使用
Fu f = new Zi();
- 抽象类
abstract class Fu {
public abstract void method();
}
class Zi extends Fu {
public void method(){
System.out.println(“重写父类抽象方法”);
}
}
//类的多态使用
Fu fu= new Zi();
- 接口
interface Fu {
public abstract void method();
}
class Zi implements Fu {
public void method(){
System.out.println(“重写接口抽象方法”);
}
}
//接口的多态使用
Fu fu = new Zi();
规则: 父类引用指向子类对象
前期绑定: 在编译期 父类引用能调用的属性或者方法都是父类的
后期绑定: 在运行期 ,父类引用调用的方法,如果子类重写了这个方法,调用的就是子类的方法
绑定机制:
静态绑定(前期绑定):属性,private ,static ,final 修饰的都为前期绑定
动态绑定(后期绑定):程序运行时,执行的方法,如果子类重写过,执行的就为重写过的方法
类型转换问题:
将父类引用转化为子类引用,只能再多态才能转换,不然会报ClassCastException(运行时异常)
子类对象转为父类引用不用强转
好处:
- 消除类型之间的耦合关系
- 可替换性
- 可扩充性
- 接口性
- 灵活性
- 简化性
实践代码,搓搓小手动起来(=!=):
public class Animal1 {
public String name = "动物";
public void fun(){
System.out.println("hello 动物");
}
}
public class Cat extends Animal1{
public String name = "猫";
public void fun(){
System.out.println("hello 猫");
}
}
public class Pig extends Animal1 {
public String name = "猪";
public void fun(){
System.out.println("hello 猪");
}
}
import java.util.Scanner;
public class Animal1Test {
public static void main(String[] args) {
Animal1Test test = new Animal1Test();
Scanner sc = new Scanner(System.in);
System.out.println("请输入你选择领养的宠物:(猪或猫)");
String str = sc.next();
test.getAnimal(str);
}
//领养动物
public void getAnimal(String str){
Animal1 pet = null;
if (str.equals("猪")) {
pet = new Pig();//父类引用指向子类对象
} else if (str.equals("猫")) {
pet = new Cat();//父类引用指向子类对象
} else {
System.out.println("请重新选择");
}
pet.fun();//调用子类的fun()
}
}
输出结果:
今天学习分享的内容到此为止了,本是想要激励自己学下去,但发现快乐更是知识在于分享!
作为初学者对于Java的理解实在浅薄,如有错误请指正,万分感谢!