面向对象
面向对象的理解
面向对象是相对面向过程而言,面向过程,强调的是功能行为
面向对象和面向过程都是一种思想
面向对象,将功能封装进对象,强调具备了功能的对象。
面向对象是基于面向过程的。
面向对象的三个特征:封装,继承,多态
类与对象的关系
类:就是对现实生活中事物的描述
对象:就是这类事物,实实在在存在的个体
想要描述:提取对象中共性内容,对具体的抽象
描述时:这些对象的共性有,姓名,年龄,性别,学习Java功能。
描述在Java中是用类的形式来体现的,映射到Java中,描述就是class定义的类。
对象在Java中是通过new操作符所产生的一个实体,这个实体在堆内存中。
成员变量和局部变量
作用范围:
成员变量作用于整个类中。
局部变量作用于函数或者语句中。
在内存中的位置:
成员变量:在堆内存中,因为对象的存在,才在内存中存在
局部变量:在栈内存中。
匿名对象
匿名对象是对象的简化形式
匿名对象两种使用情况:
a 当对对象方法仅进行一次调用时
b 匿名对象可以作为实际参数进行传递
封装
封装:是指隐藏对象的属性和实现细节,仅对外提供公共访问方式
好处:将变化隔离,便于使用,提高重用性,提高安全性。
封装原则:
1 将不需要对外提供的内容都隐藏起来。
2 把属性都隐藏,提供公共方法对其访问。
private关键字
是一个权限修饰符,用于修饰成员(成员变量和成员函数)
被私有化的成员只在本类中有效
常用于将成员变量私有化,对外提供对应的set和get方法对其进行访问,提高对数据访问的安全性。
构造函数
对象一被建立(通过new关键字),就会调用与之相应的构造函数,构造函数的作用就是给对象进行初始化。
特点:
Static关键字
1. 函数名与类名相同
2. 不用定义返回值类型
3. 不可以写return语句
注意:
1. 默认构造函数的特点。
默认构造函数是隐式的。
默认构造函数是空参数的构造函数。
默认构造函数的权限和所属类一致,随着类权限的变化而变化。
2. 多个构造函数是以重载的形式存在的。
3. 当一个类中没有定义构造函数时,系统会默认给该类加入一个空参数的构造函数(默认构造函数)。
当自定义了构造函数后,默认的构造函数就没有了。
构造函数和一般函数的区别:
写法不同
运行不同:
构造函数是在对象一建立(new)就运行,给对象初始化
一般函数是对象调用才执行,给对象添加对象具备的功能。
一个对象建立,构造函数只运行一次。
一般函数可以被该对象调用多次。
什么时候定义构造函数呢?
当分析事物时,该事物存在就具备一些特性或者行为,那么将这些内容定义在构造函数中。
构造代码块
构造代码块中定义的是不同对象共性的初始化内容。
作用:给对象进行初始化(对象一建立就运行,而且优先于构造函数)
构造代码块和构造函数的区别:
构造代码块是给所有对象进行统一初始化。
构造函数是给对应的对象初始化。
this关键字
this: 代表本类的对象,
this代表它所在函数所属对象的引用
可简单理解为:哪个对象在调用this所在的函数,this就代表哪个对象。
this的应用:
1 当定义类中功能时,该函数内部要用到调用该函数的对象时,这时用this来表示这个对象。
2 但凡本类功能内部使用了本类对象,都用this表示
this关键字在构造函数间调用
this语句:用于构造函数之间互相调用
构造函数之间在互相调用时只能使用this
this语句只能定义在构造函数的第一行。因为初始化要先执行。
示例代码:
class Person {
private String name;
private int age;
Person(){
}
Person(String name){
this.name = name;
}
Person(String name,int age){
// this.name = name;
this(name);
this.age = age;
}
}
class Demo{
public static void main(String[] args){
Person p = new Person("lisi",30);
}
}
Static关键字
用于修饰成员(成员变量和成员函数)
被修饰后的成员具备以下特点:
1 随着类的加载而加载,随着类的消失而消失,生命周期最长。
2 优先于对象存在:静态是先存在的,对象是后存在的。
3 被所有对象所共享
4 可以直接被类名调用: 类名.静态成员
实例变量(对象的变量)和类变量(static修饰的变量)的区别:
1 存放位置:
类变量随着类的加载而存储在方法区中。
实例变量随着对象的建立而存储在堆内存中。
2 生命周期:
类变量生命周期最长,随着类的消失而消失。
实例变量生命周期随着对象的消失而消失。
静态的使用注意事项:
1 静态方法只能访问静态成员。
非静态方法既可以访问静态成员也可以访问非静态成员。
2 静态方法中不可以定义this和super关键字
3 主函数是静态的
静态的利弊:
利: 对对象的共享数据进行单独空间的存储,节省空间。
可以直接被类名调用。
弊: 生命周期过长。
访问出现局限性。(只能访问静态)
静态什么时候使用:(重点)
从两方面入手:因为静态修饰的内容有成员变量和成员函数。
1 何时定义静态变量(类变量)
当对象中出现共享数据(是属性值而不是属性)时,该数据需要被静态所修饰
对象中的特有数据要定义成非静态存储在堆内存中。
2 何时定义静态函数
当功能内部没有访问到非静态数据(对象特有数据)时,该功能可以定义成静态的。
示例代码:
class Person {
String name;
public static void show() {
System.out.println("haha");
}
}
class Demo {
public static void main(String[] args) {
Person p = new Person();
p.show();
Person.show();
}
}
//注释:此例中show方法并未调用name属性(非静态数据),
//这个时候需要把show方法定义成静态的通过person.show()直接调用,
//而不必new一个person对象,再通过对象调用show()方法
静态代码块
随着类的加载而执行,只执行一次,并优先于主函数,用于给类进行初始化
static{
静态代码块中的执行语句;
}
对象的初始化过程
Person p = new Person("zhangsan",20);
代码执行顺序:
1,因为new用到person.class文件,所以会先找到person.class文件并加载进内存。
2,执行该类中的static代码块,如果有的话,给person.class类进行初始化。
3,在堆内存中开辟空间,分配内存地址。
4,在堆内存中建立对象的特有属性,并进行默认初始化。
5,对属性进行显示初始化。
6,对对象进行构造代码块初始化。
7,对对象进行对应的构造函数初始化。
8,将内存地址付给栈内存中的p变量。
对象调用成员过程
![](https://img-my.csdn.net/uploads/201304/19/1366376812_7978.jpg)
多态
定义:某一类事物的多种存在形态。
例:动物中猫,狗。
猫这个对象对应的类型是猫类型
猫 x = new 猫();
同时猫也是动物的一种,也可以把猫称为动物。
动物 y = new 猫();
动物是猫和狗具体事物中抽取出来的父类型。
父类型引用指向了子类对象。
多态的体现
父类的引用指向了自己的子类对象。
父类的引用也可以接收自己的子类对象。
多态的前提
必须是类与类之间有关系,要么继承,要么实现。
通常还有一个前提:存在覆盖。
多态的好处:
多态的出现大大的提高程序的扩展性。
多态的弊端:
提高了扩展性,但是只能使用父类的引用访问父类中的成员。
使用多态的注意事项
在编译时期:参阅引用型变量所属的类中是否有调用的方法。如果有,编译通过,如果没有,编译失败。
在运行时期:参阅引用型变量所指向的对象中是否有调用的方法。
多态转型:
//多态转型演示代码
abstract class Animal1 {
abstract void eat();
}
class Cat1 extends Animal {
public void eat() {
System.out.println("吃鱼");
}
public void cathMouse() {
System.out.println("抓老鼠");
}
}
class Dog1 extends Animal {
public void eat() {
System.out.println("吃骨头");
}
public void kanJia() {
System.out.println("看家");
}
}
class DuoTaiDemo2 {
public static void main(String[] args) {
Animal a = new Cat();// 类型提升,向上转型。
a.eat();
// 如果想要调用猫的特有方法时,如何操作?
// 强制将父类的引用,转成子类类型,向下转型。
Cat c = (Cat) a;
// c.catchMouse();
// 千万不要出现这样的操作,就是将父类对象转成子类类型。
// 我们能转换的是父类应用指向了自己的子类对象时,该引用可以被提升,也可以被强制转换。
// 多态自始自终都是子类对象在做着变化。
// Animal a = new Animal();
// Cat c = (Cat)a;
function(new Cat());
function(new Dog());
}
public static void function(Animal a)// Animal a =new Cat();
{
a.eat();
//if(a instanceof Animal){ System.out.println("haha"); } else
if (a instanceof Cat) {
Cat c = (Cat) a;
// c.catchMouse();
} else if (a instanceof Dog) {
Dog c = (Dog) a;
c.kanJia();
}
}
}
多态中成员的特点
1 在多态中,非静态成员函数的特点:
在编译时期:参阅引用型变量所属的类中是否有调用的方法。如果有,编译通过,如果没有编译失败。
在运行时期:参阅对象所属的类中是否有调用的方法。
简单总结就是:成员函数在多态调用时,编译看左边,运行看右边。
2 在多态中,成员变量的特点:
无论编译和运行,都参考左边(引用型变量所属的类)。
3 在多态中,静态成员函数的特点:
无论编译和运行,都参考左边。
继承
继承的概述:将事物共性描述提取出来,单独进行描述
继承的特点
1, 提高了代码的复用性。
2, 让类与类之间产生了关系,有了这个关系,才有了多态的特性。
3, 必须是类与类之间有所属关系才可以继承,所属关系 is a .
4, Java只支持单继承,不支持多继承。
因为多继承容易带来安全隐患:
当多个父类中定义了相同功能,当功能内容不同时,不确定要运行哪一个。
但是Java中保留这种机制,并用另一种体现形式来完成表示,多实现。
5, Java支持多层继承,也就是一个继承体系
如何使用一个继承体系中的功能呢?
想要使用体系,先查阅体系父类的描述,因为父类中定义的是该体系中共性功能。
通过了解共性功能,就可以知道该体系的基本功能,该体系已经可以基本使用了。
那么在具体调用时,要创建最子类的对象,为什么呢?
一是因为有可能父类不能创建对象。
二是创建子类对象可以使用更多的功能,包括基本的也包括特有的。
简单一句话:查阅父类功能,创建子类对象使用功能。
子父类中成员的特点
子父类出现后,类中成员的特点:
1,子父类中的变量
如果子类中出现非私有的同名成员变量时,
子类要访问本类中的变量,用this
子类要访问父类中的同名变量,用super
super的使用和this的使用几乎一致
this代表的是本类对象的引用。
super代表的是父类对象的引用。
2,子父类中的函数
函数覆盖
当子类出现和父类一模一样(返回值类型,函数名,参数列表都相同)的函数时,子类对象调用该函数,会运行子类函数的内容。
如同父类的函数被覆盖一样。这种情况是函数的另一个特性:重写(覆盖)
当子类继承父类,沿袭了父类的功能,到子类中。
但是子类虽具备该功能,但是功能的内容却和父类不一致。
这时,没有必要定义新功能,而是使用覆盖特性,保留父类的功能定义,并重写功能内容。
覆盖的注意事项:
1,子类覆盖父类,必须保证子类权限大于等于父类权限,才可以覆盖,否则编译失败。
2,静态只能覆盖静态。
注意:
重载:只看同名函数的参数列表,不看返回值类型。
重写:子父类方法要一模一样,返回值类型也必须一致。
3,子父类中的构造函数
在对子类对象进行初始化时,父类的构造函数也会运行。
那是因为子类的构造函数默认第一行有一条隐式的语句 super();
super():会访问父类中空参数的构造函数,而且子类中所有的构造函数默认第一行都有一个隐式super();语句。
为什么子类一定要访问父类中的构造函数。
因为父类中的数据子类可以直接获取。所以子类对象在建立时,需要先查看父类是如何对这些数据进行初始化的。
所以子类在对象初始化时,要先访问一下父类中的构造函数。
如果要访问父类中指定的构造函数,可以通过手动定义显式super语句的方式来指定。
注意:super语句一定定义在子类构造函数的第一行。
子类的实例化过程
子类中所有的构造函数,默认都会访问父类中空参数的构造函数。
因为子类每一个构造函数内的第一行都有一句隐式super();
当父类中没有空参数的构造函数时,子类必须手动通过super语句形式来指定要访问父类中的构造函数。
当然,子类的构造函数第一行也可以手动指定this语句来访问本类中的构造函数,
子类中至少会有一个构造函数会访问父类中的构造函数。
final关键字
final:最终,作为一个修饰符。
1,可以修饰类,函数,变量。
2,被final修饰的类不可以被继承,为了避免被继承,被子类复写功能。
3,被final修饰的方法不可以被复写。
4,被final修饰的变量是一个常量,只能被赋值一次,既可以修饰成员变量,也可以修饰局部变量。
当在描述事物时,一些数据的出现值是固定的,那么这时为了增强阅读性,都给这些值起个名字,方面阅读
而这个值不需要改变,所以加上final修饰。作为常量:常量的书写规范所有字母都大写,如果
由多个单词组成。单词间通过_连接。
5,内部类定义在类中的局部位置上,只能访问该局部被final修饰的局部变量。
抽象类(abstract)
当多个类中出现相同功能,但是功能主体不同。
这是可以进行向上抽取,只抽取功能定义,而不抽取功能主体。
抽象类和一般类没有太大的不同。
该如何描述事物,就如何描述事物,只不过,该事物出现了一些看不懂的东西。这些不确定的部分,也是该事物的功能,需要明确出现,但是无法定义主体。通过抽象方法表示。
抽象类比一般类多了个抽象函数,就是在类中可以定义抽象方法,抽象类不可以实例化。
抽象类的特点:
1,抽象方法一定在抽象类中。
2,抽象方法和抽象类都必须被abstract关键字修饰。
3,抽象类不可以用new创建对象,因为调用抽象方法没意义。
4,抽象类中的抽象方法要被调用,必须由子类复写起所有的抽象方法后,建立子类对象调用。
如果子类只覆盖了部分抽象方法,那么该子类还是一个抽象类。
abstract class Student{
abstract void study()
//abstract void study();
}
class BaseStudent extends Student{
void study(){
System.out.println("base study");
}
}
class AdvStudent extends Student{
void study(){
System.out.println("adv study")
}
}
class AbstractDemo{
public static void main(String[] args){
//new Student();
new BaseStudent().study();
}
}
接口(interface)
当抽象类中的方法都是抽象的,那么该类可以通过接口的形式来表示。
接口中的成员修饰符是固定的。
成员常量:public static final
成员函数:public abstract
接口的出现将“多继承”通过另一种形式体现出来,即“多实现”。
接口定义时,格式特点:
1,接口中常见定义:常量,抽象方法。
2,接口中的成员都有固定修饰符。
常量:public static final
方法: public abstract
注:接口中的成员都是public的。
接口不可以创建对象,因为有抽象方法。
需要被子类实现,子类对接口中的抽象方法全都覆盖后,子类才可以实例化,否则子类是一个抽象类。
接口可以被类多实现,也是对多继承不支持的转换形式,java支持多实现。
接口的特点:
接口是对外暴露的规则。
接口是程序的功能扩展。
接口可以用来多实现。
类与接口之间是实现关系,而且类可以继承一个类的同时实现多个接口。
接口与接口之间可以有继承关系。
包(package)
对类文件进行分类管理
给类提供多层命名空间
写在程序文件的第一行
类名的全称是:包名.类名
包也是一种封装形式
包与包之间访问
包与包之间进行访问,被访问的包中的类以及类中的成员,需要被public修饰。
不同包中的子类还可以直接访问父类中被protected权限修饰的成员。
包与包之间使用的权限只有两种:public和protected