面向对象程序设计(Object Oriented Programming,OOP)是一种计算机编程思想——把现实世界映射到计算机模型。具体举例来讲,将现实世界中的汽车模型映射成计算机模型中的类;将现实世界中每一辆汽车映射成计算机模型中的对象;将现实中汽车的一系列参数,如颜色、价格映射成计算机模型中的属性;将现实中汽车的操作,如加速、转弯映射成计算机模型中的方法。
可见该思想是尽可能模拟人类的思维方式,使得软件的开发方法与过程尽可能接近人类认识世界、解决现实问题的方法和过程,使得描述问题的问题空间与问题的解决方案空间在结构上尽可能一致,把客观世界中的实体抽象为问题域中的对象。
面向对象程序设计可以看作一种在程序中包含各种独立而又互相调用的对象的思想,这与传统的思想刚好相反:传统的程序设计主张将程序看作一系列函数的集合,或者直接就是一系列对电脑下达的指令。面向对象程序设计中的每一个对象都应该能够接受数据、处理数据并将数据传达给其它对象,因此它们都可以被看作一个小型的“机器”,即对象。
目前已经被证实的是,面向对象程序设计推广了程序的灵活性和可维护性,并且在大型项目设计中广为应用。此外,支持者声称面向对象程序设计要比以往的做法更加便于学习,因为它能够让人们更简单地设计并维护程序,使得程序更加便于分析、设计、理解。
类是对现实生活中一类具有共同属性和行为的事物进行抽象而成的模型。
类的组成:成员变量和成员方法
//类
public class Car{
//成员变量
String color; //颜色
double price; //价格
//成员方法
public void addSpeed(){
//加速
//加速方法实现
}
}
对象是由类这个模型实例化出的一个个具体的实体。
属性:对象具有的各种特征,每个对象的每个属性都拥有特定的值。
方法:对象能够执行的操作。
创建对象格式:类名 对象名 = new 类名();
public class Car{
public static void main(String[] args) {
//对象
Car car01 = new Car();
car01.color = "Red"; //成员变量color实例化
car01.price = 99999.99; //成员变量price实例化
car01.addSpeed(); //成员方法addSpeed()实例化
}
}
类是对现实生活中一类具有共同属性和行为的事物进行抽象而成的模型。
对象是由类这个模型实例化出的一个个具体的实体。
两者关系:类是对象的抽象;对象是类的实体。
(1)类属性(静态属性):类中方法外被static修饰的变量。如果一个属性声明成类属性,那么所以的对象,都共享这么一个值。通过类和对象都可以访问类属性,但建议使用类访问属性,这样更符合语义上的理解。
(2)对象属性(非静态属性、实例属性):类中方法外没有被static修饰的变量。
(3)局部变量:类中方法中的变量。
方法(method)是将具有独立功能的代码块组织成为一个整体,使其具有特殊功能的代码集。
方法的分类:类方法(静态方法)、对象方法(非静态方法,实例方法)、构造方法
(1)类方法(静态方法):static修饰符修饰的方法。
特点:①不能调用类的对象方法;
②不能引用对象变量;
③类方法不能被重写(覆盖);
④类方法不能使用super, this关键字
(2)对象方法(非静态方法,实例方法):没有被static修饰符修饰的方法。
特点:①可以调用类的对象方法;
②可以引用对象变量;
③实例方法可以被重写(覆盖);
④实例方法可以使用super, this关键字
区别:类方法在加载类时就被分配了内存地址,因此加载后即可被任意对象调用,并且可以通过类名直接调用(类名.方法名),而实例方法需要在创建对象后才会被分配内存地址。
(3)构造方法(构造器、构造函数):构造方法是用来在创建对象时初始化对象的。当类中没有显示地定义任何构造方法时,编译器会自动给该类加上一个无参且方法体为空的默认构造方法。当类中自定义了构造方法,默认的构造方法就没有了。在一个类中可以以重载的形式定义多个构造方法,以进行不同的初始化。多个构造方法存在于类中,是以重载的形式体现的。因为构造方法的名称都相同。所以重载本质上是同一个行为具有不同的表现形式或形态能力。
除了Object类,所有类在加载过程中都需要调用父类的构造方法,所以在子类的构造方法中,需要使用super()方法隐式或显式地调用父类的构造方法。类在初始化时构造方法的调用顺序是这样的:
(1)按顺序调用父类成员变量和实例成员变量的初始化表达式;
(2)调用父类构造方法;
(3)按顺序分别调用类成员变量和实例成员变量的初始化表达式;
(4)调用类构造方法。
构造方法与普通方法的区别:
(1)普通方法用于定义对象应该具备的功能,构造方法用于定义对象应该具备的初始化内容。
(2)普通方法是在对象建立后调用该方法时才会被调用执行,构造方法是在对象建立时由JVM调用执行。
(3)构造方法的名称必须与类名一样,而普通方法只要符合标识符的命名规则就行。
(4)构造方法没有返回值类型,也不能用void修饰。
**那么是谁创建的无参构造方法呢?**是编译器。类会在java文件编译成class文件的过程中,编译器就将自动向无构造方法的类添加无参构造方法,即默认构造方法。为什么编译器要向没有定义构造方法的类添加默认构造方法?构造方法的目的就是为了初始化,既然没有显式地声明初始化的内容,则说明没有可以初始化的内容。为了在JVM的类加载过程中顺利地加载父类数据,所以就有默认构造方法这个设定。那么二者的不同之处在哪儿?二者在创建主体上的不同。无参构造方法是由开发者创建的,而默认构造方法是由编译器生成的。二者在创建方式上的不同。开发者在类中显式声明无参构造方法时,编译器不会生成默认构造方法;而默认构造方法只能在类中没有显式声明构造方法的情况下,由编译器生成。二者在创建目的上也不同。开发者在类中声明无参构造方法,是为了对类进行初始化操作;而编译器生成默认构造方法,是为了在JVM进行类加载时,能够顺利验证父类的数据信息。
class Hero {
String name; //姓名
float hp; //血量
public Hero(){
}
public Hero(String heroname){
name = heroname;
}
public Hero(String heroname,float herohp) {
name = heroname;
hp = herohp;
}
public static void main(String[] args) {
Hero h1 = new Hero();
Hero h2 = new Hero("妲己");
Hero h3 = new Hero("提莫",383);
}
}
class Car{
//类属性(静态属性)
static String copyright = "Rolls-Royce";
//对象属性(非静态属性、实例属性)
String color;
//类方法(静态方法)
static void sayMotto(){
System.out.println("走进千万家");
}
//对象方法(非静态方法、实例方法)
void addSpeed(){
System.out.println("我要加速了");
}
public static void main(String[] args) {
//局部变量
int age = 99;
Car car = new Car();
System.out.println(Car.copyright + car.copyright);
Car.sayMotto(); car.sayMotto();
//类方法和类属性都可以通过类和对象进行访问
System.out.println(car.color);
car.addSpeed();
//对象方法和对象属性只能通过对象进行访问
//对象方法可以访问对象属性和类属性
//类方法只能访问类属性
}
}
(4)方法参数
方法的参数:形参和实参、基本类型参数和引用类型参数、返回值与参数。
实际参数(实参、argument):在调用函数时传递给函数的参数。必须具有确定的值,以便把这些值传送给形参。
形式参数(形参、parameter):在定义函数时使用的参数,用来接收调用该函数时传入的实参。由于它不是实际存在的变量,也称虚拟变量。
基本数据类型参数:形式参数的改变,不影响实际参数的值。
引用类型参数:形式参数的改变,影响实际参数的值。
定义方法时要明确:返回值类型(主要是明确方法操作完毕之后是否有数据返回)、明确参数(主要是明确参数的类型和数量)。
类名作为形参和返回值:方法的形参是类名,其实需要的是该类的对象;方法的返回值是类名,其实返回的是该类的对象。
抽象类名作为形参和返回值:方法的形参是抽象类名,其实需要的是该抽象类的子类对象;方法的返回值是抽象类名,其实返回的是该抽象类的子类对象。
接口名作为形参和返回值:方法的形参是接口名,其实需要的是该接口的实现类对象;方法的返回值是接口名,其实返回的是该接口的实现类对象
class Car{
int speed = 100;
public static void main(String[] args) {
Car car = new Car();
//调用方法时,void类型的方法,直接调用即可
car.addSpeed01();
//非void类型的方法,推荐用变量接收调用
int newSpeed = car.addSpeed02();
car.addSpeed03(10);
int newSpeed01 = car.addSpeed04(10);
}
//无参数无返回值
public void addSpeed01(){
System.out.println("加速了");
}
//无参数有返回值
public int addSpeed02(){
return speed+ 10;
}
//有参数无返回值
public void addSpeed03(int number){
speed = speed + number;
System.out.println("增加了"+number+"速度");
}
//有参数有返回值
public int addSpeed04(int number){
return speed + number;
}
}
(5)方法重写与方法重载
重写(Override)
重写是子类对父类中允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。
class Animal{
public void move(){
System.out.println("动物可以移动");
}
}
class Dog extends Animal{
public void move(){
//当需要在子类中调用父类的被重写方法时,要使用 super 关键字。
super.move(); // 应用super类的方法
System.out.println("狗可以跑和走");
}
}
public class TestDog{
public static void main(String args[]){
Animal a = new Animal(); // Animal 对象
Animal b = new Dog(); // Dog 对象
a.move();// 执行 Animal 类的方法 //动物可以移动
b.move();//执行 Dog 类的方法 //狗可以跑和走
}
}
方法的重写规则:
(1)参数列表与被重写方法的参数列表必须完全相同。
(2)返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类。
(3)访问权限不能比父类中被重写方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。
(4)父类的成员方法只能被它的子类重写。
(5)声明为 final 的方法不能被重写。
(6)声明为 static 的方法不能被重写,但是能够被再次声明。
(7)构造方法不能被重写。
(8)子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
(9)子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
(10)重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更宽泛的强制性异常。例如: 父类 的一个方法申明了一个检查异常 IOException,但是在重写这个方法的时候不能抛出 Exception 异常,因为 Exception 是 IOException 的父类,只能抛出 IOException 的 子类异常。
(11)如果不能继承一个类,则不能重写该类的方法。
重载(Overload)
重载(overloading) 是在一个类里面,方法名字相同,而参数不同。即每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。最常用的地方就是构造器的重载。
重载规则:
(1)被重载的方法必须改变参数列表(参数个数或类型不一样);
(2)方法重载与返回类型和访问修饰符无关,无法以返回值类型作为重载函数的区分标准。
(3)被重载的方法可以声明新的或更广的检查异常;
(4)方法能够在同一个类中或者在一个子类中被重载。
public class Overloading {
public int test(){
System.out.println("test1");
return 1;
}
public void test(int a){
System.out.println("test2");
}
//以下两个参数类型顺序不同
public String test(int a,String s){
System.out.println("test3");
return "returntest3";
}
public String test(String s,int a){
System.out.println("test4");
return "returntest4";
}
public static void main(String[] args){
Overloading o = new Overloading();
System.out.println(o.test());
o.test(1);
System.out.println(o.test(1,"test3"));
System.out.println(o.test("test4",1));
}
}
重写与重载之间的区别
区别点 | 重载方法 | 重写方法 |
---|---|---|
参数列表 | 必须修改 | 一定不能修改 |
返回类型 | 可以修改 | 一定不能修改 |
异常 | 可以修改 | 可以减少或删除,一定不能抛出新的或者更广的异常 |
访问 | 可以修改 | 一定不能做更严格的限制(可以降低限制) |
总结:方法的重写(Overriding)和重载(Overloading)是java多态性的不同表现,重写是父类与子类之间多态性的一种表现,重载可以理解成多态的具体表现形式。
(1)方法重载是一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同,则称为方法的重载(Overloading)。
(2)方法重写是在子类存在方法与父类的方法的名字相同,而且参数的个数与类型一样,返回值也一样的方法,就称为重写(Overriding)。