目录
概念:
- 面向对象是一种思想
- 是相对于面向过程而言,是基于面向过程的,面向过程强调的是功能行为,面向对象是将功能封装进对象,强调具备了功能的对象
核心特性:
封装、继承,多态
类和对象
类:对现实生活中事物的描述,映射到java中,描述就是class定义的类
对象:就是这类事物,实实在在存在的个体,具体对象就是对应java在堆内存中用new建立实体
匿名对象
匿名对象是对象的简化形式
两种使用情况
- 当对对象方法仅进行一次调用的时候
- 匿名对象可以作为实际参数进行传递
匿名对象有属性有方法,但是给匿名对象属性赋值没有意义,运行到;匿名对象就被销毁了,如
new Person().name=li; //创建对象,赋完值就销毁了
new Person().age=18; //在此创建对象,在赋值,在销毁,没有意义
匿名对象作为实际参数传递和对象作为实际参数对比
main()
{
Car c=new Car();
show(c); //运行完这一句之后,堆中的实体还是有执行,main中的c指向,所以还不是垃圾,需要等main方法执行完,就成了垃圾,等待回收
show(new Car()); //运行期间不是垃圾,因为有方法中的c所指向,运行完这句代码,堆中的实体没有引用,成为了垃圾,等待回收
}
public static void show(Car c)
{
c.num=3;
c.color="red"
c.run();
}
封装:
概念:
是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。
好处:
- 将变化隔离
- 便于使用
- 提高重用性
- 提高安全性
封装的原则:
- 将不需要对外提供的内容都隐藏起来
- 把属性都隐藏起来,提供公共方法对其访问
如何实现?
我们将属性隐藏起来,属性值就不能与外界产生联系,既要隐藏还要提供公共方法对其访问,于是我们就有了get,set对属性进行访问控制,而且可以在访问方式中加入逻辑判断等语句,对访问的数据进行操作,提高代码健壮性。
代码逻辑:外界可以通过访问一下方法传递参数到类中公共的方法中,进行逻辑判断后,通过this关键字,重新对类中隐藏的属性赋值
构造函数
特点:
- 函数名和类名相同
- 不用定义返回值类型
- 不可以写return语句
- 可以重载
作用:
给对象进行初始化,对象一建立就会调用与之对应的构造函数
隐藏机制:
当一个类中没有定义构造函数时,那么系统会默认给类加入一个空参数的构造函数,这个默认的构造函数的权限和所属类一致,如果类被public修饰,那么默认的构造函数也带public修饰符,如果类没有被public修饰,那么默认的构造函数,也没有pubilc修饰,默认构造函数的权限是随着类的变化而变化的
当在类中自定义了构造函数后,默认的构造函数就没有了
构造函数和一般函数的不同
- 构造函数是在对象一建立就运行,给对象初始化
- 而一般函数是对象调用才执行,是给对象添加对象具备的功能
- 一个对象建立,构造函数只运行一次
- 而一般方法可以被该对象调用多次
构造代码块:
作用:
给对象进行初始化
特点:
- 对象一建立就运行,而且优先于构造函数执行
- 构造代码块中定义的是不同对象共性的初始化内容
构造代码块和构造函数的区别:
构造代码块是给所有对象进行统一初始化,而构造函数是给对应的对象初始化,因为对象的名字都不一样,所以构造函数具有唯一性,但是构造代码块没有名字,所以会所有的对象初始化
this:
- 代表本类的对象,代表它所在函数所属对象的引用,简单说:哪个对象在调用this所在的函数,this就代表哪个对象
- 可以区分局部变量和成员变量同名的情况
- 可以用于构造函数之前进行互相调用,这样的情况只能定义在构造函数的第一行,因为初始化要先执行
class Person
{
Person()
{
this("li")
}
Person(String name)
{
this();
this.name=name;
}
}
Static关键字
用法:
用于修饰成员(成员变量和成员函数)
被修饰的成员具备以下特点:
- 随着类的加载而加载:静态会随着类的消失而消失,生命周期长
- 优先于对象的存在:静态是存在,对象是后存在
- 被所有对象所共享
- 可以直接被类名调用,也可以被对象调用
注意
- 静态方法只能访问静态成员:非静态方法既可以访问静态也可以访问非静态,因为非静态方法的建立,说明静态成员已经建立了,但是静态方法的建立,可能对象还没有创建,非静态方法也就不可能创建
- 静态方法中不可以写this,super关键字:因为静态优先于对象存在,所以静态方法中不可以出现this
- 主函数是静态的
静态代码块:
static
{
静态代码块中的执行语句
}
特点:
随着类的加载而执行,只执行一次(不管在new几个对象,都只执行一次),并优先于主函数,用于给类进行初始化
Person p =new Person("张三",20);
/*
这句话做了什么事情?
1.因为new用到了Person.class,所以会先找到Person.class文件并加载到内存中
2.执行该类中static代码块,如果有的话,给Person.class类进行初始化
3.在堆内存中开辟空间,分配内存地址
4.在堆内存中建立对象的特有属性,并进行默认初始化
5.对属性进行显示初始化,如:private String name="li";成员变量
6.对对象进行构造代码块初始化
7.对对象进行对应的构造函数初始化
8.将内存地址赋给栈内存中的p变量
*/
静态代码块:有些代码必须在项目启动的时候就执行,这种代码是主动执行的(当类被载入时,静态代码块被执行,且只被执行一次,静态块常用来执行类属性的初始化)
静态方法:需要在项目启动的时候就初始化,在不创建对象的情况下,这种代码是被动执行的(静态方法在类加载的时候就已经加载 可以用类名直接调用)。
两者的区别是:静态代码块是自动执行的,
静态方法是被调用的时候才执行的.
静态代码块,在虚拟机加载类的时候就会加载执行,而且只执行一次;
非静态代码块,在创建对象的时候(即new一个对象的时候)执行,每次创建对象都会执行一次
不创建类执行静态方法并不会导致静态代码块或非静态代码块的执行
静态代码块和静态变量执行按照先后顺序执行(自动执行),静态方法时被调用的时候才执行,但是已经被初始化了
实例变量和类变量的区别:
1.存放位置
- 类变量随着类的加载而存在于方法区中
- 实例变量随着对象的建立而存在堆内存中
2.声明周期
- 类变量声明周期最长,随着类的消失而消失
- 实例变量声明周期随着对象的消失而消失
静态利弊:
利处:对对象的共享数据进行单独空间的存储,节约空间,没有必要每一个对象都存储一份
弊端:声明周期过长,访问出现局限性(静态只能访问静态)
工具类
每一个应用程序中都有共性的功能,可以将这些功能进行抽取,独立封装以便复用(封装)
当我们发现对象是用于封装数据的,但是我们的工具类中的对象并未封装特有数据,而且操作我们工具类对象的也没有用到我们里面特有的数据,这就要考虑让程序更加严谨,是不需要对象的,可以将类中的方法定义为static,直接通过类名调用(静态类)
将方法都静态后,可以方便于使用,但是该类还是可以被其他程序建立对象的(还是可以new的),为了更为严谨,强制让该类不能建立对象,可以通过构造函数私有化完成(外界不可以new了,只能类名.方法名)
我们写好工具类之后,需要发布给其他人使用需要有一个手册,通过在代码中的特殊标识,可以用javadoc自动生成帮助文档
/**
@param arr 接受一个int类型的数组
@param a 要置换的位置
...
、
继承:
概念:
是一种关系的描述
好处:
提高了代码的复用性
让类与类之间产生了关系,有了这个关系,才有了多态的特性
知识点:
java中只支持单继承,不支持多继承
因为多继承容易带来安全隐患:当多个父类中定义了相同的功能,当功能内容不同时,子类对象不确定要运行哪一个,但是java保留了这种机制,并用另一种体现形式来完成表示,多实现。
java支持多层继承。也就是一个继承体系
如何使用一个继承体系中的功能呢?
想要使用体系,先查阅体系父类的描述,因为父类中定义的是该体系中的共性功能,通过了解共性功能,就可以知道该体系的基本功能,哪呢这个体系已经可以基本使用了。那么在具体调用时,要创建最子类的对象,为什么呢?
一是因为有可能父类不能创建对象
二是创建子类对象可以使用更多的功能,包括基本的也包括特有的
总结:查阅父类功能,创建子类对象使用功能
父子之间有相同的属性和方法
相同的属性:通过this(本类对象的引用)和super(父类对象的引用)来区分
相同的方法:通过this和super来区分,也可以通过重写
子类实现时,子类构造函数中默认在上面加了super(),如果父类没有无参数的构造函数会报错
重写:
概念:
当子类继承父类,沿袭了父类的功能到子类中,但是子类虽具备该功能,但是功能的内容却和父类不一致,这时没有必要定义新功能,而是使用覆盖,保留父类的功能定义,并重写功能内容
重写要求:
1.子类覆盖父类,必须保证子类权限大于等于父类权限,才可以覆盖,否则编译失败
2.静态只能覆盖静态
调用父类构造函数
在对子类对象进行初始化时,父类的构造函数也会运行,那是因为子类的构造函数默认第一行有一条隐式的语句 super()
super()会访问父类中空参数的构造函数,而且子类中所有的构造函数默认第一行都是super();
为什么子类一定要访问父类中的构造函数?
因为父类中的数据子类可以直接获取,所以子类对象在建立时,需要先查看父类是如何对这些数据进行初始化的,所以子类在对象初始化时,要先访问一下父类中的构造函数,如果要访问父类中指定的构造函数,可以通过手动定义super语句的方式来指定
注意:
super语句一定要定义在子类构造函数的第一行
当父类中没有空参数的构造函数时,子类必须手动通过super语句形式来指定要访问父类中的构造函数
当然,子类的构造函数第一行也可以手动指定this语句来访问本类中的构造函数,子类中至少有一个构造函数会访问父类中的构造函数
继承是对封装性的一个破坏,为了保证封装性和继承,用final来实现
final
- 可以修饰类,方法,变量
- final修饰的类不可以被继承,为了避免被继承,被子类重写功能
- final修饰的方法不可以被重写
- final修饰的变量是一个常量,只能被赋值一次,既可以修饰成员变量,也可以修饰局部变量
- 内部类只能访问被final修饰的局部变量
抽象:
概念:
- 当多个类中出现相同的功能,但是功能主体不同,这是可以向上抽取,这时,只抽取功能定义,而不抽取功能主体。
- 描述事物的时候,该事物出现了一些看不懂的东西,这些不确定的部分,也是该事物的功能,需要明确的出现,但是无法定义主体,就通过抽象方法来表示
特点:
- 抽象方法一定在抽象类中
- 抽象方法和抽象类都必须被abstract关键字修饰
- 抽象类不可以用new创建对象,因为调用抽象方法没有意义,因为里面都没有功能主体,也是因为实例化没有意义,如学生类,这个实例化没有意义,但是实例化某个学生就有意义了
- 抽象类中的抽象方法要被使用,必须由子类复写起所有的抽象方法后,建立子类对象调用
继承实现了基本功能的继承,但是我想要一些有规则的扩展功能怎么办,就用到了接口
接口
基础知识:
interface{}
接口中的成员修饰符是固定的,接口中的成员都是public的,可以省略不写,jvm会自动加上
成员常量:public static final
成员函数:public abstract
接口的出现将“多继承”通过另一种形式体现出来,即“多实现”
接口是不可以创建对象的,因为有抽象方法,需要被子类实现,子类对接口中的抽象方法全部覆盖后,子类才可以实例化,否则子类是一个抽象类
接口可以被多实现,那如果出现多个接口中出现同名方法,那不就和多继承出现一样的问题么?
不会出现同样的问题,因为接口中的方法不会声明方法体,而多继承有方法体,接口中的方法可以由子类任意定义
接口可以多继承,但是不能声明不同返回类型的同名方法
特点:
接口是对外暴露的规则
接口是程序的功能扩展
接口可以用来多实现
类与接口之间是实现关系,而且类可以继承一个类的同时实现多个接口
接口与接口之间可以由继承关系
多态
概念:
某一类事物的多种存在形态
例如猫这个对象对应的类型是猫类型,同时猫也是动物的一种,也可以把猫称为动物
动物 y=new 猫();
动物是猫和其他动物中抽取出来的父类型,父类型引用指向了子类对象
多态的体现:
- 父类的引用指向了自己的子类对象
- 父类的引用也可以接收自己的子类对象
多态的前提:
- 必须是类与类之间有关系,要么继承,要么实现,存在覆盖
多态的好处:
- 多态的出现大大的提高程序的扩展性
多态的弊端:
- 提高了扩展性,但是只能使用父类的引用访问父类中的成员
多态的应用:
public static void main(String[] args)
{
Animal a =new Cat(); //Cat类型提升,向上转型
a.eat();
//如果想要调用猫的特有方法时,如何操作?
//强制将父类的引用,转成子类类型,向下转型
Cat c=(Cat)a;
c.catchMouse();
}
在多态中成员函数的特点:
在编译期间:参阅引用型变量所属的类中是否有调用的方法,如果有,编译通过,如果没有编译失败
在运行期间:参阅对象所属的类中是否有调用的方法
简单总结:成员函数在多态调用时,编译看左边,运行看右边
public static void main(String[] args)
{
//Fu中有方法m1,m2,zi中有m3
Fu f=new zi()
f.m1();
f.m2();
f.m3(); //编译的时候这个会失败
}
在多态中成员变量的特点:
无论编译和运行,都参考左边(引用型变量所属的类)
因为在堆中生成的对象中会有两个地方,一个存父类的属性,一个存子类的属性,而父类引用指向的是父类的地址,所以获取的属性也是父类的属性
public static void main(String[] args)
{
//Fu中的num为5
Fu f=new zi();
System.out.println(f.num);
//zi中的num为8
zi f=new zi();
System.out.println(f.num);
}
结果:5 8
在多态中,静态成员函数的特点:
无论编译和运行,都参考左边,因为静态成员是静态绑定,非静态成员是动态绑定,有覆盖操作
内部类:
概念:
将一个类定义在另一个类的里面,对里面那个类就称为内部类(内置类,嵌套类)
什么时候用:
当描述事物时,事物的内部还有事物,该事物用内部类来描述。因为内部事物在使用外部事物的内容
class Body
{
private class XinZang
public void show()
{
new XinZang();
}
}
访问规则:
内部类可以直接访问外部类中的成员,包括私有成员,之所以可以直接访问外部类中的成员,是因为内部类中持有一个外部类的引用,格式:外部类名.this
而外部类要访问内部类中的成员必须要建立内部类的对象
访问格式:
1.当内部类定义在外部类的成员位置上,而且非私有,可以在外部其他类中,可以直接建立内部类对象
格式:
//外部类名.内部类名 变量名=外部类对象.内部类对象;
Outer,Inner in = new Outer().new Inner();
2.当内部类在成员位置上,就可以被成员修饰符所修饰
比如:private:将内部类在外部类中进行封装
static:内部类就具备static的特性
当内部类被static修饰后,只能直接访问外部类中的static成员,出现访问局限性
在外部其他类中,如何直接访问static内部类的非静态成员呢?
new Outer.Inner().function();
在外部其他类,如何访问Static内部类的静态成员呢?
Outer.Inner.function();
注意:当内部类中定义了静态成员,该内部类必须是static的
当外部类中的静态方法访问内部类时,内部类也必须是static的
内部类定义在局部时
1.不可以被成员修饰符修饰
2.可以直接访问外部类中的成员,因为还持有外部类中的引用,但是不可以访问它所在的局部中的变量,只能访问被final修饰的局部变量
class Outer
{
int x=3;
void method()
{
final int y=4;
class Inner
{
void function()
{
System.out.println(x); //3
System.out.println(y); //4
}
}
}
}
匿名内部类:
1.匿名内部类其实就是内部类的简写格式
2.定义匿名内部类的前提
内部类必须是继承一个类或者实现接口
3.匿名内部类的格式:new 父类或者接口(){定义子类的内容}
4.其实匿名内部类就是一个匿名子类对象。而且这个对象有点胖,可以理解为带内容的对象
5.匿名内部类中定义的方法最好不要超过3个