一,面向对象简介
1. 面向对象概念
- 面向对象是相对面向过程而言,面向对象和面向过程都是一种思想。
- 面向过程,强调的是功能行为;面向对象,将功能封装进对象,强调具备了功能的对象。
2. 基本特征(3个):
封装(Encapsulation):将对象实现细节隐藏,通过一些共用方法来暴露对象的功能;提高类的内聚性,降低了对象之间的耦合性;
- 私有仅仅是封装的一种表现形式,提供对外访问方式(set和get方法)可以对数据进行操作判断,提高安全性
- 包也是一种封装形式
- 包与包之间的类进行访问,被访问的包中的类必须是public的,被访问的包中的类的方法也必须是public的。
继承(Inheritance):类之间共享属性和操作的机制(子类继承父类,直接获得父类的属性和方法,实现类复用);通过extends关键字让类与类之间产生继承关系
- 特点:
- 子类可以直接访问父类中的非私有的属性和行为,子类无法继承父类中私有的内容(方法和变量)
- 提高了代码的复用性
- Java只支持单继承,不支持多继承,但支持多层继承
- 继承代码示例:
- 特点:
class Fu {
Fu() {
super();
show();// 该方法被子类复写,调用的是子类的show方法,此时其成员变量num还未进行显示初始化
}
void show() {
System.out.println("fu show");
}
}
class Zi extends Fu {
int num = 8;
Zi() {
super();// 通过super初始化父类内容时,子类的成员变量并未显示初始化,等super()父类初始化完毕后,才进行子类的成员变量显示初始化
}
void show() {
System.out.println("zi show..." + num);
}
}
class ExtendsDemo {
public static void main(String[] args) {
Zi z = new Zi();
z.show();
}
}
运行结果:
多态(Polymorphism):子类对象直接赋给父类变量,运行时依然表现子类特征。
- 定义:某一类事物的多种存在形态。
- 作用:多态的存在提高了程序的扩展性和后期可维护性(弊端:前期定义的内容不能调用后期子类的特有内容)
多态时,成员的特点:
成员函数(非静态):示例中为method1()、method2()和method3()。
- 在编译时期:参考引用型变量所属的类中是否有调用的函数(有,编译通过(method1()被复写,method2()为父类);没有,编译失败(如method3()父类中不存在))。
- 在运行时期:参考的是对象所属的类中是否有调用的函数
- 简单说:编译看左边,运行看右边。
静态成员函数:实例中的method4()。
继承中,父类为static的方法无法被覆盖- 编译时:参考的是对象所属的类中是否有调用的函数
- 运行时:参考的是对象所属的类中是否有调用的函数
- 简单说:编译和运行看左边。
成员变量(静态和非静态):非静态的str1与静态的str2。
- 在编译时:参考引用型变量所属的类中是否有调用的成员变量(有,编译通过,没有,编译失败)。
- 在运行时:参考引用型变量所属的类中是否有调用的成员变量,并运行该所属类中的成员变量。
- 简单说:编译和运行都参考等号的左边。
代码示例:
/*
多态中成员(函数和变量)调用特点示例:
*/
class BaseClass {
String str1 = "父类非静态成员变量";
static String str2 = "父类静态成员变量";
void method1() {
System.out.println("base method_1");
}
void method2() {
System.out.println("base method_2");
}
static void method4() {
System.out.println("static base method_4");
}
}
class SubClass extends BaseClass {
String str1 = "子类非静态成员变量";
static String str2 = "子类静态成员变量";
//复写父类的method1()方法
void method1() {
System.out.println("sub method_1");
}
void method3() {
System.out.println("sub method_3");
}
static void method4() {
System.out.println("static sub method_4");
}
}
class PolymorphismDemo {
public static void main(String[] args) {
System.out.println("多态中非静态成员函数调用示例:");
BaseClass bs = new SubClass();
bs.method1(); //sub method_1
bs.method2(); //base method_2
// f.method3(); //编译不能通过,因父类没有method3()函数
System.out.println("----------------------- ");
System.out.println("多态中静态成员函数调用示例:父类");
bs.method4(); //static base method_4
System.out.println("--------------------------");
System.out.println("多态中成员变量调用示例:均为父类");
System.out.println(bs.str1); //父类非静态成员变量
System.out.println(bs.str2); //父类静态成员变量
}
}
以下为运行结果:
- 开发和设计过程
- 开发过程:不断创建对象、使用对象和指挥对象做事
- 设计过程:管理和维护对象之间的关系
二,类与对象
1. 类:
具有共同属性(成员变量)、共同方法(成员函数)的一类事物
内部类:
将一个类定义在另一个类的里面,里面那个类就称为内部类(内置类,嵌套类)。
- 特点:
- 内部类可以直接访问外部类中的成员,包括私有成员。
- 而外部类要访问内部类中的成员必须要建立内部类的对象。
- 定义时机:分析事物时,发现该事物描述中还有事物,而且这个事物还在访问被描述事物的内容,这时候就定义内部类。
访问格式:
当内部类定义在外部类的成员位置上,而且非私有,可以在外部其他类中直接建立内部类对象
格式:
外部类名.内部类名 变量名=外部类对象.内部类对象;
e.g.: Outer.Inner in=new Outer().new Inner();当内部类在成员位置上,就可以被成员修饰符所修饰。比如:private:将内部类在外部类中进行封装当内部类被static修饰后,只能访问外部类中的static成员。出现了访问局限
在外部其他类中,内部类为静态,直接访问static内部类的非静态成员
格式:new Outer.Inner().function();
在外部其他类中,内部类为静态,直接访问static内部类的静态成员:可以不用创建内部类对象,直接调用
格式:Outer.Inner.function();
内部类能直接访问外部类中的成员的原因:因为内部类持有了外部类的引用,外部类名.this
注意:
- 当内部类中定义了静态成员,该内部类必须是static的
- 当外部类中的静态方法访问内部类时,内部类也必须是static的。
- 内部类定义在局部位置上,也可以直接访问外部类中的成员。同时可以访问所在局部中的局部变量,但必须是被final修饰的。
匿名内部类:
- 定义:内部类的简化写法
- 前提:内部类可以继承或实现一个外部类或者接口
格式:
new 父类构造器([实参列表]) 或 接口() { //匿名内部类的类体部分 }
抽象类:
- 定义:Java中定义只有功能声明,没有方法体的方法,该方法的具体实现由子类完成,该方法称为抽象方法,包含抽象方法的类就是抽象类。
格式:
修饰符 abstract 返回值类型 函数名(参数列表);
特点:
- 抽象类和抽象方法必须用abstract关键字来修饰
- 抽象类不可以被实例化,也就是不可以用new创建对象
- 抽象类需通过其子类实例化,而子类需要覆盖掉抽象类中所有的抽象方法后才可以创建对象,因此抽象类一定是父类
- 抽象类中可以定义抽象方法和非抽象方法,子类继承后,可以直接使用非抽象方法
接口:
- 概念:当一个抽象类中的方法都是抽象的时候,这时可以将该抽象类用另一种形式定义和表示,就是接口。
格式:
interface{}
- 接口中的成员修饰符是固定的:
1. 成员常量:public static final
2. 成员函数:public abstract 返回值类型 函数名(参数列表);
- 接口中的成员修饰符是固定的:
特点:
- 接口中的成员都是公共的权限
- 接口是对外暴露的规则,接口是程序的功能扩展
- 接口的出现将“多继承”通过另一种形式体现出来,即“多实现”
- 一个类在继承另一个类的同时,还可以实现多个接口。
- 类与类之间是继承关系,类与接口直接是实现关系,可以多实现
- 接口与接口之间是继承关系,而且接口可以多继承
- 接口中只能定义抽象方法,必须由子类去实现
成员变量和局部变量区别:
成员变量(2种):实例Field(不以static修饰)和类Field(以static修饰):
- 范围:定义在类中,在整个类中都可以被访问。
- 存储:实例Field**堆内存**(随对象的建立而建立,随对象对象被回收而释放);
类Field(静态变量)存储在方法区(共享数据区)的静态区,随着类的加载而存在,随着类的消失而消失。 - 默认初始化值:有。
局部变量(3种):形参(方法签名中定义的变量)、方法局部变量(在方法内定义)和代码块局部变量(在代码块定义)
- 范围:只定义在局部范围内(3处)只在所属的区域有效。
- 存储:栈内存中(作用的范围结束,变量空间会自动释放)。
- 默认初始化值:无。
2. 对象:
类的具体存在,该类事物实实在在存在的个体。
- 对象的产生:创建对象的根本途径是构造器(特殊的方法),通过new关键字来调用某个类的构造器即可创建这个类的实例。
匿名对象:
- 对象的简化形式(一个没有名字的对象, 创建了一个对象出来,没有赋给一个变量);使用情形:当对对象方法仅一次调用时;作为实际参数进行传递时。
基本数据类型参数及引用数据类型参数传递比较示例:
//基本数据类型参数传递
class Demo {
public static void main(String[] args){
int x = 3; //方法局部变量
show(x);
/*输出结果为3,因为show()方法中的x为方法局部变量,当主函数调用结束后,show方法出栈。
show方法参数x也随之出栈。main方法打印x的值。此时x指的是main方法中的x变量的值。
*/
System.out.println("x=" + x);
}
public static void show(int x){
x = 4; //方法局部变量
}
}
//引用数据类型参数传递
class Demo {
int x = 3; //实例Feild,整个类中都可以访问。
public static void main(String[] args){
Demo d = new Demo();
d.x = 9;
show(d); //show方法的参数d和main方法中的变量d同时指向了堆内存中同一个实体对象,堆内存中的实体对象的x属性值变为4。
/*show方法出栈,show方法参数d也随之出栈,main方法的变量d依然引用着堆内存中的实体对象,
因此堆内存中的实体对象不会被垃圾回收器清除。
*/
System.out.println(d.x);//输出结果为4
}
public static void show(Demo d){
d.x = 4;
}
}
2. 两者关系
- 类是对象的抽象,对象是类的实例。
三,构造器和初始化块
1. 构造器(构造函数)
简介:
一个用于创建实例时,执行初始化的特殊方法。
构造器是一个类创建对象的根本途径,系统默认为类提供一个空参的构造器(其执行体为空),当创建一个对象时,系统默认初始化,将基本类型的Field设为0(数值型)或false(布尔型),引用类型的Feild设为null;若想改变这种默认初始化,即可使用构造器来实现。
一旦自定义构造器后,系统将不会提供默认的构造器(一般建议为该类额外编写一个无参的做保留)。
特点:
- 函数名与类名相同
- 不用定义返回值类型(在构造函数前面加上返回值就只是一般函数了)
- 没有具体的返回值
- 多个构造函数是以重载的形式存在的
- 继承中,子类构造函数执行时,父类构造函数也运行了(原因:在子类的构造函数中,第一行有一个默认的隐式语句:super())
作用:
- 给对象进行初始化。
定义时机:
- 分析事物,该事物具备一些特性或者行为,就把把这些内容定义到构造函数中,若需要参数进行运算就定义参数。
2. 初始化块(构造代码块)
关于局部代码块:局部代码块可以定义局部变量的生命周期
class IfDemo{
public static void main(String[] args){
{
int m = 89;
System.out.println("Hello World..." + m);
}
/*
*变量m是局部变量,生命周期仅在局部代码块括号中,当代码执行到局部代码块右括号外,变量m便消失了。
*局部代码块外的代码当然也就访问不到变量m了。
*/
//System.out.println("over..." + m);
}
}
作用:
- 给对象初始化,定义的是不同对象共性的初始化内容对象一建立就运行,而且优先于构造函数执行。
与构造函数区别:
- 构造代码块是给所有对象进行统一初始化,而构造函数是给对应的对象初始化。
代码示例:
class Person {
private String name;
private int age;
//构造代码块,可以给所有对象进行初始化;
{
System.out.println("Running!");
}
Person() {
System.out.println("nane="+name+"...age="+age);
}
Person(String name) {
this.name=name;//this 代表它所在函数所属对象的引用:哪个对象在调用this所在函数,this就代表哪个对象,该处为作用一
System.out.println("nane="+name+"...age="+age);
}
Person(String name,int a) {
//this.name=name; //该句将少打印"name:null...age:0"
this(name); //this(name),this语句用于构造函数之间的调用,将打印"name:null...age:0",this语句只能定义在构造函数的第一行,因为初始化要先执行,该处为作用二
age=a;
System.out.println("nane="+name+"...age="+age);
}
public void setAge(int a) {
if(a>0 && a<130)
age =a;
else
System.out.println("age非法!");
}
public int getAge() {
return age;
}
public boolean campare(Person p) {
return this.age==p.age;//函数内部要用到调用该函数的对象时,这时用this来表示这个对象,该处为作用一
}
}
class PersonDemo2 {
public static void main(String[] args) {
Person p1=new Person();
Person p2=new Person("小明");
p2.setAge(25);
System.out.println(p2.getAge());
Person p3=new Person("小亮",26);
Person p4=new Person("小贤",24);
System.out.println(p4.campare(p3));
}
}
运行结果: