Java面向对象
面向对象与面向过程的区别
面向过程 | 面向对象 | |
---|---|---|
设计思路 | 自顶向下、层次化、分解 | 自底向上、对象化、综合 |
程序单元 | 函数模块 | 对象 |
设计方法 | 程序=算法+数据结构 | 程序=对象=数据+方法 |
优点 | 相互独立,代码共享,性能相对较高 | 接近人的思维方式, 使用灵活,易维护、易复用、易扩展 |
缺点 | 修改、维护困难 | 性能相对较低 |
1.面向对象的概念
面向对象编程是一种编程范式,它使用“对象”来设计软件。对象可以包含数据(通常称为属性或字段)和代码(通常称为方法)。在Java中,几乎一切都是对象,包括基本数据类型(如int和char)都是对象的特例。
面向对象编程的四个主要原则是:
1.封装(Encapsulation):封装是关于隐藏对象的内部状态和行为细节,只暴露必要的操作接口。在Java中,这通常通过使用访问修饰符(如private, protected, public)来实现。
2.继承(Inheritance):继承允许创建一个类(称为子类或派生类)来继承另一个类(称为父类或基类)的属性和方法。这有助于代码复用和创建层次结构。Java支持单继承,即一个类只能直接继承自一个父类,但可以通过接口实现多重继承的效果。
3.多态(Polymorphism):多态意味着同一个操作作用于不同的对象,可以有不同的解释或不同的行为。在Java中,多态主要通过方法重载(overloading)和方法重写(overriding)来实现。多态性允许我们编写更通用的代码,可以处理不同类型的对象。
4.抽象(Abstraction):抽象是简化复杂现实世界问题的过程,只保留与当前问题相关的属性和方法。在Java中,抽象可以通过抽象类和接口来实现。抽象类可以包含抽象方法(没有具体实现的方法),而接口则定义了一组方法规范,由实现该接口的类来具体实现。
1.1如何建立面向对象的思维呢?
1、先整体,再局部
2、先抽象,再具体
3、能做什么,再怎么做
1.2面向对象的优势
- 可重用性:代码重复使用,减少代码量,提高开发效率。面向对象的三大基本特征(继承、封装和多态)都围绕这个核心。
- 可扩展性:指新的功能可以很容易地加入到系统中来,便于软件的修改。
- 客观理性:能够将功能与数据结合,方便管理。
2.类与对象
类(Class):
- 定义对象的蓝图,包括属性和方法。
- 示例:
public class Car { ... }
类:用来描述一类具有相同特征(属性)和相同行为(方法)的对象。
对象(Object)
对象(Object)是系统中用来描述客观事物的一个实体,它是构成系统的一个基本单位。
它们是有形的,如一个人、一件物品;也可以是无形的,如一个计划、一次交易。
类和对象的关系:
- 类是对象的抽象
- 对象是类的实例化
定义格式:
类的定义:
class 类名称{
属性名称;
返回值类型 方法名称(){}
}对象的定义:
一个类要想真正的进行操作,则必须依靠对象,对象的定义格式如下:
类名称 对象名称 = new 类名称() ;如果要想访问类中的属性或方法(方法的定义),则可以依靠以下的语法形式:
访问类中的属性:对象.属性 ;
调用类中的方法:对象.方法();
全局变量(成员变量) 定义在方法的外部,类的内部。使用范围是整个类 不需要初始值 存储在堆内存中(对象存在时才在存在) 局部变量 (方法中的变量)
定义在方法的内部或者某一个语句块的内部,适用范围仅限于方法内或者语句块内 必须有初始值 存储在栈内存中 成员方法 语法:
访问修饰符 返回值类型 方法名称(参数列表){ 方法体 }
访问修饰符:public 返回值类型由返回值决定 成员变量可以直接在成员方法中使用,但是main方法中如果调用成员变量和方法必须通过对象.属性名\方法名(参数列表)的形式来调用
成员方法之间的调用,直接写方法名(参数列表)即可
构造方法 对象一建立就会调用构造方法,可以创建对象,给成员变量(属性)初始化。
方法名和类名相同 没有返回值和void,没有return 不能被static等关键字修饰 可以方法重载(定义多个参数列表不同的构造方法) 当一个类中没有写构造方法时,系统会默认给该类一个默认的无参构造方法。当自己定义构造方法后默认的构造方法就不存在了。(自己定义有参的构造方法后,如果还想使用无参的构造方法,就要自己再添加一个无参的构造方法)
public class Animal {
String name;
int age;
//构造方法
public Animal(){
System.out.println("------无参的构造方法------");
name = "动物";
age = 0;
}
public Animal(String name,int age){
System.out.println("------有参的构造方法------");
this.name = name;
this.age = age;
}
public static void main(String[] args) {
//创建对象
Animal a1 = new Animal();
System.out.println(a1.name+","+a1.age);
Animal a2 = new Animal("兔子",2);
System.out.println(a2.name+","+a2.age);
}
}
在没有static关键字修饰的方法中使用
this关键的字用来区分成员变量和局部变量同名的情况:
public class Student{
//成员变量
String name;
int age;
int classNum;
public Student(String name,int age,int classNum){
//s1调用了构造方法,所以这里的this代表s1对象
this.name = name; //成员变量name=局部变量name
this.age = age;
this.classNum = classNum;
}
public static void main(String[] args){
Student s1 = new Student("张三",18,1);
System.out.println(s1.name);//打印 张三
}
}
this关键字在构造方法中的第一行以 this(参数列表) 的形式出现时,就表示当前构造方法调用了该类中其他的构造方法(于参数列表相匹配的构造方法):
public class Test1 {
public Test1(){
this("张三",18);
}
public Test1(String name,int age){
System.out.println("姓名:"+name+",年龄:"+age);
}
public static void main(String[] args) {
Test1 test1 = new Test1();
}
}
3.方法的重写和重载
方法的重写规则
-
参数列表与被重写方法的参数列表必须完全相同。
-
返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。
-
访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。
-
父类的成员方法只能被它的子类重写。
-
声明为 final 的方法不能被重写。
-
声明为 static 的方法不能被重写,但是能够被再次声明。
-
子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
-
子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
-
重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
-
构造方法不能被重写。
-
如果不能继承一个类,则不能重写该类的方法。
重载(Overload)
重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
最常用的地方就是构造器的重载。
重载规则:
-
被重载的方法必须改变参数列表(参数个数或类型不一样);
-
被重载的方法可以改变返回类型;
-
被重载的方法可以改变访问修饰符;
-
被重载的方法可以声明新的或更广的检查异常;
-
方法能够在同一个类中或者在一个子类中被重载。
-
无法以返回值类型作为重载函数的区分标准。
重载与重写的区别
区别点 重载方法 重写方法 参数列表 必须修改 一定不能修改 返回类型 可以修改 一定不能修改 异常 可以修改 可以减少或删除,一定不能抛出新的或者更广的异常 访问 可以修改 一定不能做更严格的限制(可以降低限制)
-
4.封装
封装的优点
-
- 良好的封装能够减少耦合。
-
- 类内部的结构可以自由修改。
-
- 可以对成员变量进行更精确的控制。
-
- 隐藏信息,实现细节。封装性是面向对象思想的三大特征之一,封装就是隐藏实现细节,仅对外提供访问接口。实现细节部份包装、隐藏起来的方法。
封装有:属性的封装、方法的封装、类的封装、组件的封装、模块化封装、系统级封装…
封装的好处:模块化、信息隐藏、代码重用、插件化易于调试、具有安全性
封装的缺点:会影响执行效率封装之前:属性都可以直接访问和修改
class Person{
String name;
int age;
}封装之后:
class Person{
//属性是成员变量,私有化属性,使得其他对象不能直接访问属性
private String name;
private int age;
//参数及方法内定义的变量是局部变量
public void setName(String name){
this.name = name;}
public String getName(){
return name;}
}成员变量和局部变量的区别
a、在类中的位置不同
成员变量:在类中定义
局部变量:在方法中定义或者方法的参数
b、在内存中的位置不同
成员变量:在堆内存(成员变量属于对象,对象进堆内存)
局部变量:在栈内存(局部变量属于方法,方法进栈内存)
c、生命周期不同
成员变量:随着对象的创建而存在,随着对象的销毁而消失
局部变量:随着方法的调用而存在,随着方法的调用完毕而消失
d、初始化值不同
成员变量:有默认初始化值,引用类型默认为null
局部变量:没有默认初始化值,必须定义,赋值,然后才能使用
注意:
局部变量名称可以和成员变量名称一样,在方法中使用的时候,采用的是就近原则。
- 隐藏信息,实现细节。封装性是面向对象思想的三大特征之一,封装就是隐藏实现细节,仅对外提供访问接口。实现细节部份包装、隐藏起来的方法。
5.继承
继承的特性:
-
子类拥有父类非 private 的属性、方法。
-
子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
-
子类可以用自己的方式实现父类的方法。
-
Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 B 类继承 A 类,C 类继承 B 类,所以按照关系就是 B 类是 C 类的父类,A 类是 B 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。
-
提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。
继承关键字
继承可以使用 extends 和 implements 这两个关键字来实现继承,而且所有的类都是继承于 java.lang.Object,当一个类没有继承的两个关键字,则默认继承 Object(这个类在 java.lang 包中,所以不需要 import)祖先类。
继承中的构造方法 创建有继承关系的子类对象时,会先执行父类中默认的构造方法,然后执行子类中相关的构造方法 如果父类中不存在默认的构造方法,那么此时的解决问题,第一种就是手动添加一个默认的构造方法。 super关键字 表示当前类的父类的引用 只能出现在有继承关系的子类中 super两种用法: super.属性名、super.方法名(参数列表) 表示父类的属性和方法,和子类中的属性或方法重名时使用 super(参数列表) 出现在子类构造方法的第一句代码时(super不能与this连用) 就是通过参数列表匹配父类的构造方法来创建父类对象
super 与 this 关键字
**super 关键字:**我们可以通过 super 关键字来实现对父类成员的访问,用来引用当前对象的父类。
**this 关键字:**指向自己的引用,引用当前对象,即它所在的方法或构造函数所属的对象实例。。
final 关键字
final 可以用来修饰变量(包括类属性、对象属性、局部变量和形参)、方法(包括类方法和对象方法)和类。
final 含义为 "最终的"。
使用 final 关键字声明类,就是把类定义定义为最终类,不能被继承,或者用于修饰方法,该方法不能被子类重写:
-
声明类:
final class 类名 {//类体}
-
声明方法:
修饰符(public/private/default/protected) final 返回值类型 方法名(){//方法体}
构造器
子类是不继承父类的构造器(构造方法或者构造函数)的,它只是调用(隐式或显式)。如果父类的构造器带有参数,则必须在子类的构造器中显式地通过 super 关键字调用父类的构造器并配以适当的参数列表。
如果父类构造器没有参数,则在子类的构造器中不需要使用 super 关键字调用父类构造器,系统会自动调用父类的无参构造器。
class SuperClass {
private int n;
// 无参数构造器
public SuperClass() {
System.out.println("SuperClass()");
}
// 带参数构造器
public SuperClass(int n) {
System.out.println("SuperClass(int n)");
this.n = n;
}
}
// SubClass 类继承
class SubClass extends SuperClass {
private int n;
// 无参数构造器,自动调用父类的无参数构造器
public SubClass() {
System.out.println("SubClass()");
}
// 带参数构造器,调用父类中带有参数的构造器
public SubClass(int n) {
super(300);
System.out.println("SubClass(int n): " + n);
this.n = n;
}
}
// SubClass2 类继承
class SubClass2 extends SuperClass {
private int n;
// 无参数构造器,调用父类中带有参数的构造器
public SubClass2() {
super(300);
System.out.println("SubClass2()");
}
// 带参数构造器,自动调用父类的无参数构造器
public SubClass2(int n) {
System.out.println("SubClass2(int n): " + n);
this.n = n;
}
}
public class TestSuperSub {
public static void main(String[] args) {
System.out.println("------SubClass 类继承------");
SubClass sc1 = new SubClass();
SubClass sc2 = new SubClass(100);
System.out.println("------SubClass2 类继承------");
SubClass2 sc3 = new SubClass2();
SubClass2 sc4 = new SubClass2(200);
}
}
6.多态
多态就是同一函数在不同类中有不同的实现;
- 面向对象的多态性,即“一个接口,多个方法”。
- 多态性体现在父类中定义的属性和方法被子类继承后,可以具有不同的属性或表现方式。
- 多态性允许一个接口被多个同类使用,弥补了单继承的不足。
多态的实现方式
方式一:重写:方式二:接口方式三:抽象类和抽象方法