引言
面向对象是一种编程思想,是程序员思考问题的思维方式,以此为编程思想指导的程序设计,又称为面向对象程序设计( Object-Oriented Programing ,OOP ),面向对象设计设计思想,关注点为对象( 任何事物,万事万物 ),而非具体过程。
面向过程编程思想:也是一种编程思维,它关注的就是问题解决的步骤和实现过程,这种编程思维往往涉及到细节数据结构和算法本身。
对于不同编程思想,有不同的语言,C语言就是典型面向过程的设计语言,Java 就是面向对象的程序设计语言。
程序:
模拟现实世界 解决现实问题 而使用计算机编程语言编写的指令集合
1.面向对象:
世界由无数个对象组成;<br /> 一切客观存在的事物都是对象,任何对象都有自己的特征和行为;<br /> <br /> 特征:代表对象有什么<br /> 行为:代表对象能做什么
一个类创建多个对象:
类:定义了对象应有的特征和行为 ,类是对象的模板 <br /> 对象:拥有多个特征和行为的实体,对象是类的实例<br /> <br /> <br /> ** 局部变量 ** ** 实例变量**<br /> <br /> 方法或方法内的结构中。 类的内部,方法的外部<br /> 无默认值 与数组相同<br /> 从定义行到包含其结构结束 本类有效<br /> 不允许与局部变量重名 不允许与实例变量重名,可与局部变量重名,局部变量优先
对象内存分布
对象是自定义类的实例,自定义类是一种引用数据类型,所以这里和前面章节学的数组一样,对象也是一种引用数据类型,它的存储必然也分堆栈,对象本质在堆内申请空间,对象引用被存在栈里面。
图:
类与对象的关系
类和对象的关系就是,抽象与具体的关系,图纸与实物的关系,一个类可以实例化出多个对象,每个对象拥有独立的内存空间,尤其是实例属性,各自有一份。
图:
一个类可以 实例化出 N 个 多个对象
2.类的定义:
属性:变量表示 ,(实例变量)类的内部 方法的外部;
方法:实例方法 不再书写static
方法重载:一个类定义多个相同名称的方法
要求:<br /> 方法名相同,参数列表不同(类型 个数 顺序 ), 与访问修饰符 返回值类型 无关,<br /> 好处:灵活 方便 屏蔽使用差异;<br /> 同名不同参<br />
构造方法:
系统默认提供无参构造方法;<br /> 当你定义一个有参构造时,必须定义一个无参构造;<br /> 方法名与类名相同;<br /> 无参构造方法;<br /> 使用有参构造方法 为属性赋值(初始化数据成员);
this关键字:
this.name = name; 表示把局部变量赋值给实例变量
this(xxx)访问本类中的其他方法;
本类 同包 非同包子类 其他
private 1 0 0 0
default 1 1 0 0
protected 1 1 1 0
public 1 1 1 1
3.面向对象的三大特点
封装:
private:意为私有的意思,指的是如果一个属性 或者 方法被其修饰,那么这个属性和方法就只能在其声明的类中使<br /> 定义一个类,该类有一个成员变量,通过构造方法将其进行赋初值,<br /> 并提供该成员的getXXX()和setXXX(参数)方法<br /> 修改访问修饰符,并提供公共的get/set方法
class Foo{
private int age;
public void aa(){
}
}
class TestFoo{
public static void main(String[] args){
Foo foo = new Foo();
foo.age=100; //报错,此时age是无法被访问的。
}
}
继承:
**extends:**<br /> 产生继承关系之后,子类可以使用父类的属性和方法,子类也可以定义自己的方法和属性
**好处**:提高代码的复用性,提高代码的可拓展性;<br /> 单继承,一个类只能有一个直接父类,但可以多级继承<br /> 构建子类对象先构建父类对象<br /> <br /> <br /> **不可继承**:<br />**类中的构造方法,只负责创建本类对象,不可继承 可用super去调用**<br /> private修饰的属性和方法<br /> 父子类不在同一个包时,default修饰的属性和方法,(仅同包可见)<br /> <br /> 方法的重写(覆盖):<br />参数列表与被重写方法的参数列表必须完全相同。<br />返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。<br /> 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。<br />父类的成员方法只能被它的子类重写。<br />声明为 final 的方法不能被重写。<br />声明为 static 的方法不能被重写,但是能够被再次声明。<br />子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。<br />子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。<br />或者比被重写方法声明的更广泛的强制性异常,反之则可以。<br />**构造方法不能被重写**。<br />如果不能继承一个类,则不能重写该类的方法。<br />** **<br />** 1.发生方法重写的两个方法返回值、方法名、参数列表必须完全一致(子类重写父类的方法)。**<br />**2.子类抛出的异常下不能超过父类相应方法抛出的异常(子类异常不能大于父类异常)。**<br />**3.子类方法的访问级别不能低于父类相应方法的访问级别(子类访问级别不能低于父类访问级别)。**<br /> <br /> 区别点 重载方法 重写方法<br /> 参数列表 必须修改 一定不能修改<br /> 返回类型 可以修改 一定不能修改<br /> 异常 可以修改 可以减少或删除,一定不能抛出新的或者更广的异常<br /> 访问 可以修改 一定不能做更严格的限制(可以降低限制<br /> @Override 它是一个注解,用来检验方法是否重写正确。它不是必要的,建议加上防止重写错误。
public class Animal {
protected String name;
protected int age;
protected String sex;
public Animal(){
}
public Animal(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
public void eat(){
System.out.println("吃");
}
public void sleep(){
System.out.println("睡");
}
}
public class Dog extends Animal{
//共性属性和方法无需定义了,继承就可以了
//子类特有的属性和行为
String color;
public void lookDoor(){
System.out.println("看门");
}
//重新继承的方法
@Override
public void eat() {
System.out.println("吃骨头");
}
@Override
public void sleep() {
System.out.println("躺着睡");
}
}
super 关键字
如果子类 存在方法重写 和 属性遮蔽 , 可以通过 super.xxx 来强制区分。
/**
* 理解Super的作用
*/
public class SuperDemo {
public static void main(String[] args) {
Son son = new Son();
son.happy();
}
}
class Father{
String name="张飞";
int age;
public void happy(){
System.out.println("打麻将.....");
}
}
class Son extends Father{
String hobby;
String name="刘备";
@Override
public void happy() {
super.happy();
System.out.println("蹦迪......"+super.name);
}
}
多态:
多态是同一个行为具有多个不同表现形式或形态的能力。
优点: 1. 消除类型之间的耦合关系
2. 可替换性
3. 可扩充性
4. 接口性
5. 灵活性
6. 简化性
多态存在的三个必要条件
继承(直接或者间接)
重写
父类引用指向子类对象:Parent p = new Child();
父类引用尽可以调用父类声明的属性或者方法,不可调用子类独有的属性或者方法
子类重写了父类的方法,以父类类型引用调用此方法时,优先执行子类中重写的方法(遵循重写原则)
父类作为方法形参 实现多态
//主人
public class Master {
/* 这两个方法重载,可以使用多态优化为一个方法
//接收一个 Dog 类型的参数
public void feed( Dog obj ){
obj.eat();
}
//接收一个 Cat 类型的参数
public void feed( Cat obj ){
obj.eat();
}
*/
//接收一个 Animal 类型的参数
public void feed( Animal obj ){ //Animal obj = cat = new Cat();
obj.eat();
}
public static void main(String[] args) {
Dog dog = new Dog();
Cat cat = new Cat();
Master master = new Master();
master.feed( cat ); // 这里可以传 cat 和 dog对象 均可。
}
}
父类作为返回值返回 实现多态
//主人
public class Master {
//接收一个 Animal 类型的参数
public void feed( Animal obj ){
obj.eat();
}
//返回值为父类类型
public Animal kill(){
Random random = new Random();
int num = random.nextInt(2);
if( num == 0 ){ //杀个狗
Dog dog = new Dog();
return dog;
}else{ //杀个猫
Cat cat = new Cat();
return cat;
}
}
}
**3.父类型定义数组,可以保存各种子类对象**
public static void main(String[] args) {
Animal[] arr = new Animal[4];
arr[0] = new Cat();
arr[1] = new Dog();
arr[2] = new Cat();
arr[3] = new Dog();
for( Animal an : arr){
an.eat();
}
}
多态好处:屏蔽子类间的差异,灵活 耦合度低
public static void main(String[] args) {
show(new Cat()); // 以 Cat 对象调用 show 方法
show(new Dog()); // 以 Dog 对象调用 show 方法
Animal a = new Cat(); // 向上转型
a.eat(); // 调用的是 Cat 的 eat
Cat c = (Cat)a; // 向下转型
c.work(); // 调用的是 Cat 的 work
}
public static void show(Animal a) {
a.eat();
// 类型判断
if (a instanceof Cat) { // 猫做的事情
Cat c = (Cat)a;
c.work();
} else if (a instanceof Dog) { // 狗做的事情
Dog c = (Dog)a;
c.work();
}
}
instanceof 关键字
作用是判断一个引用是否为指定的类型 语法为 引用 instanceof 类型 如果该表达式成立返回 true ,否则返回false
public static void main(String[] args) {
System.out.println("--------转型-----------");
// 自动类型提升 : 安全 自动完成
Animal an = new Dog();
// 向下转型 : 不安全 手动完成
//Dog dd = (Dog)an; //java.lang.ClassCastException: case6.Cat cannot be cast to case6.Dog
//dd.lookDoor();
// 转型前判断一下
if( an instanceof Cat ){
Cat xx= (Cat)an;
xx.catchMouse();
}
if( an instanceof Dog ){
Dog yy = (Dog)an;
yy.lookDoor();
}
}
4.三个关键字
abstract:抽象
抽象类:不完整不具体的类,所以不能被实例化;
package case4;
/**
* 动物: 抽象类
*/
public abstract class Animal { // 这里使用 abstract 关键字修饰
protected String name;
public Animal() {
}
public Animal(String name) {
this.name = name;
}
public void sleep(){
// 这里的方法比较抽象,无法给出具体实现,这里不编写任何代码,称为空实现
}
public void eat(){
// 这里的方法比较抽象,无法给出具体实现,这里不编写任何代码,称为空实现
}
}
抽象方法:没有方法体,大括号都不用给,不能被实现,子类必须重写,否则子类还是抽象类;
package case4;
/**
* 动物: 抽象类
*/
public abstract class Animal { // 这里使用 abstract 关键字修饰
protected String name;
public Animal() {
}
public Animal(String name) {
this.name = name;
}
public abstract void sleep(); // 这里使用 abstract 修饰方法,设计为抽象方法,不给出具体实现,代码更简洁了
public abstract void eat(); // 这里使用 abstract 修饰方法,设计为抽象方法,不给出具体实现,代码更简洁了
}
抽象类中不一定有抽象方法,有抽象方法类的必须是抽象类<br /> 不可以new 自己的类<br />**抽象类的作用**:<br />抽象类和普通类一样,可以定义普通类全部的内容。少了创建本身实例的能力,多了可以定义抽象方法的作用。可以作为父类使用,可以更好更简洁的使用多态的灵活性,代码更精简。<br />
static:静态的
实例属性:每个对象各自持有的独立空间(多份),对象单方面修改,不会影响其他对象;<br /> 静态属性:是整个类共同持有的共享空间,任何对象修改,都会影响其他对象;<br /> 静态可以修饰属性和方法;成为静态属性(类属性),静态方法(类方法)<br /> 静态成员是全类所有对象共有享的成员<br /> 可以直接类名访问。<br />
/**
* 学生
*/
public class Student {
String name; //名字:实例属性
static String nation; //国籍: 静态属性
//实例方法 : 可以访问静态和实例属性
public void show(){
System.out.println("静态属性nation"+nation+"实例属性"+name);
}
}
package case6;
public class TestStudent {
public static void main(String[] args) {
//同意赋值
Student.nation="新加坡";
Student s1 = new Student();
s1.name="张三";
s1.show();
System.out.println("------------------");
Student s2 = new Student();
s2.name = "李四";
s2.show();
}
}
静态方法允许直接访问静态成员,不能直接访问非静态成员
静态方法中不允许使用this/super 关键字
静态方法可以继承,不能重写,没有多态;
package case6;
/**
* 学生
*/
public class Student {
String name; //名字:实例属性
static String nation; //国籍: 静态属性
//实例方法 : 可以访问静态和实例属性?
public void show(){
System.out.println("静态属性nation"+nation+"实例属性"+name);
}
//定义一个静态方法 : 静态方法不能访问实例相关的数据(this 实例属性)
public static void study(){
//System.out.println("我是"+this.name + "我来之"+nation); 报错
System.out.println("我来之"+nation);
}
}
public class TestStudent {
public static void main(String[] args) {
//访问静态属性
Student.nation="新加坡";
//访问静态方法
Student.study();
}
}
普通代码块
普通代码块,是实例级别的信息,随着对象创建而执行,且每创建一个对象都会执行,执行多次,用于初始化实例属性,当然这是可选的,因为为实例属性赋值的方式可以有其他方式,比如构造器赋值,set方法属性赋值等。
动态代码块:创建对象时,出发动态代码块执行,
执行地位:初始化属性之后,构造方法代码之前
作用: 可为实例属性赋值,或必要的初始行为;
public class Foo {
String name = "SB"; //实例属性 setp 1
{ //普通代码块(动态代码块) setp 2
System.out.println("代码块"+Foo.name);
name = "NB";
}
public Foo() { //构造器 setp 3
System.out.println("构造器....."+name);
}
public static void main(String[] args) {
Foo f1 = new Foo();
Foo f2 = new Foo();
}
}
可以总结 属性执行赋值 > 普通代码块执行> 构造器执行。
类加载:JVM首次使用某个类时,需要通过CLASSPATH查找该类的.class 文件
加载时机:创建对象 创建子类对象 访问静态属性 调用静态方法 主动加载 ClASS.forName(“全限定名”);
静态代码块:类加载时执行(执行一次)
执行地位:在静态属性初始化之后
作用: 可为静态属性赋值,或必要的初始行为;
public class Foo {
static String name = "SB"; //1 // 类加载(1加载 2验证 3准备 4初始初始化)
static {
System.out.println("代码块"+Foo.name);
name = "NB"; //2
}
public Foo() { //3
System.out.println("构造器....."+name);
}
public static void main(String[] args) {
Foo f1 = new Foo();
System.out.println(Foo.name);
}
}
- 加载: Foo.class 装载到内存(读文件)
- 验证: 验证是否是一个合法字节码,以及相关的语法校验。
- 准备: 开辟所需要的空间。
- 初始化: 从上到下异常执行赋值语句。
面试题对象初始化过程
目前,咱们初始化属性可能涉及的方式就很丰富了,直接赋值 ,动态代码块赋值 ,静态代码块赋值,构造器赋值,那么如果类之间存在继承关系时,属性初始化执行先后顺序是如何的呢? ```java package case8;
/**
面试题: 对象创建 各初始化模块执行的先后顺序 */ public class Demo {
public static void main(String[] args) {
Son son = new Son();
}
}
// 爷爷类 class YeYe{ { System.out.println(“爷爷动态代码块”); } static { System.out.println(“爷爷静态代码块”); }
public YeYe() {
System.out.println("爷爷构造器");
}
} // 父类 class Father extends YeYe{
{
System.out.println("父类动态代码块");
}
static {
System.out.println("父类静态代码块");
}
public Father() {
System.out.println("父类构造器");
}
}
// 子类 class Son extends Father{
{
System.out.println("Son动态代码块");
}
static {
System.out.println("Son静态代码块");
}
public Son() {
System.out.println("Son构造器");
}
}
```
输出: 递归向上执行静态的 静态代码块 -> 递归返回执行各级 动态代码块 和 构造器。
爷爷静态代码块
父类静态代码块
Son静态代码块
爷爷动态代码块
爷爷构造器
父类动态代码块
父类构造器
Son动态代码块
Son构造器
JDK 的类库中,就有充分使用static 案例, Math 数据工具类,提供了很多与数学运算相关的属性和方法( 都是静态的 )。
final:最后的,不可更改的
可修饰 类 方法 变量<br /> <br /> final 类:String Math System 此类不能被继承<br /> <br /> final 方法:不支持子类以覆盖的方式修改 可以重载<br /> <br /> final 变量:此变量值只能被赋值一次,不能被改变<br /> <br /> 局部常量:显式初始化之后<br /> <br /> 实例常量 :final String name; 不在提供默认值 必须手动赋予初始值;<br /> 赋值时机 :显式初始化,动态代码块,构造方法;<br /> <br /> 静态常量 :static final String name;不在提供默认值 必须手动赋予初始值;<br /> 赋值时机 :显示初始化,静态代码块<br /> <br /> 对象常量:final修饰基本类型常量 值不可变<br /> final修饰引用类型常量 地址不可变