黑马程序员_面向对象

时间:2023-02-18 17:04:29
------- android培训、java培训、期待与您交流! ---------- 面向对象三个特征:封装,继承,多态。

类和对象的关系:
  • 现实生活中的对象:张三 李四。
  • 想要描述:提取对象*性内容。对具体的抽象。
  • 描述时:这些对象的共性有:姓名,年龄,性别,学习java功能。
  • 映射到java中,描述就是class定义的类。
  • 具体对象就是对应java在堆内存中用new建立实体。
  • 类就是:对现实生活中事物的描述。
  • 对象:就是这类事物,实实在在存在个体。
成员变量和局部变量。
作用范围。

  • 成员变量作用于整个类中。
  • 局部变量变量作用于函数中,或者语句中。
在内存中的位置:
  • 成员变量:在堆内存中,因为对象的存在,才在内存中存在。
  • 局部变量:存在栈内存中。
匿名对象使用
  • 方式一:当对对象的方法只调用一次时,可以用匿名对象来完成,这样写比较简化。
  • 如果对一个对象进行多个成员调用,必须给这个对象起个名字。
  • 方式二:可以将匿名对象作为实际参数进行传递。

private :
  • 私有,权限修饰符:用于修饰类中的成员(成员变量,成员函数)。
  • 私有只在本类中有效。
  • 将成员变量私有化以后,类以外即使建立了对象也不能直接访问。但是人应该有年龄,就需要在=类中提供对应访问成员变量的方式。
  • 注意:私有仅仅是封装的一种表现形式。
  • 之所以对外提供访问方式,就因为可以在访问方式中加入逻辑判断等语句。对访问的数据进行操作。提高代码健壮性。

对象一建立就会调用与之对应的构造函数。
构造函数的作用:可以用于给对象进行初始化。

构造函数的小细节:

  • 当一个类中没有定义构造函数时,那么系统会默认给该类加入一个空参数的构造函数。
  • 当在类中自定义了构造函数后,默认的构造函数就没有了。
构造函数:
  • 构造函数和一般函数在写法上有不同。
  • 在运行上也有不同。
  • 构造函数是在对象一建立就运行。给对象初始化。
  • 而一般方法是对象调用才执行,给是对象添加对象具备的功能。
  • 一个对象建立,构造函数只运行一次。
  • 而一般方法可以被该对象调用多次。
  • 一个类中默认会有一个空参数的构造函数,
  • 这个默认的构造函数的权限和所属类一致。
  • 如果类被public修饰,那么默认的构造函数也带public修饰符。
  • 如果类没有被public修饰,那么默认的构造函数,也没有public修饰。


    默认构造构造函数的权限是随着的类的变化而变化的。
什么时候定义构造函数呢?
  • 当分析事物时,该事物存在具备一些特性或者行为,那么将这些内容定义在构造函数中。
构造代码块。
  • 作用:给对象进行初始化。
  • 对象一建立就运行,而且优先于构造函数执行。
  • 和构造函数的区别:
  • 构造代码块是给所有对象进行统一初始化,
  • 而构造函数是给对应的对象初始化。
  • 构造代码快中定义的是不同对象共性的初始化内容。
class Person
{
private String name;
private int age;

public Person(){}
public Person()
{
name = "shaw";
System.out.println("B: name="+name+",,age="+age);
}
}

this关键字:
  • this:看上去,是用于区*部变量和成员变量同名情况。
  • this为什么可以解决这个问题?
  • this到底代表的是什么呢?
  • this就代表本类的对象,到底代表哪一个呢?
  • this代表它所在函数所属对象的引用。
  • 简单说:哪个对象在调用this所在的函数,this就代表哪个对象。

this的应用
  • 当定义类中功能时,该函数内部要用到调用该函数的对象时,这时用this来表示这个对象。
  • 但凡本类功能内部使用了了本类对象,都用this表示。
class Person
{
private String name;
private int age;
Person(int age)
{
this.age = age;
}
Person(String name)
{
this.name = name;
}
Person(String name,int age)
{
this.name = name;
this.age = age;
}

public void speak()
{
System.out.println("name="+this.name+"...age="+this.age);
this.show();
}
public void show()
{
System.out.println(this.name);
}

/*
需求:给人定义一个用于比较年龄是否相同的功能。也就是是否是同龄人。
*/
public boolean compare(Person p)
{
return this.age==p.age;

}
}
  • this语句 :用于构造函数之间进行互相调用。
  • this语句只能定义在构造函数的第一行。因为初始化要先执行。

静态:static。
  • 用法:是一个修饰符,用于修饰成员(成员变量,成员函数).
  • 当成员被静态修饰后,就多了一个调用方式,除了可以被对象调用外,
  • 还可以直接被类名调用。类名.静态成员。
static特点:
  • 1,随着类的加载而加载。 也就说:静态会随着类的消失而消失。说明它的生命周期最长。
  • 2,优先于的对象存在明确一点:静态是先存在。对象是后存在的。
  • 3,被所有对象所共享
  • 4,可以直接被类名所调用。
实例变量和类变量的区别:
存放位置。
  • 类变量随着类的加载而存在于方法区中。
  • 实例变量随着对象的建立而存在于堆内存中。
生命周期:
  • 类变量生命周期最长,随着类的消失而消失。
  • 实例变量生命周期随着对象的消失而消失。
静态使用注意事项:
  • 静态方法只能访问静态成员。非静态方法既可以访问静态也可以访问非静态。
  • 静态方法中不可以定义this,super关键字。因为静态优先于对象存在。所以静态方法中不可以出现this。
  • 主函数是静态的。

静态有利有弊
  • 利处:对对象的共享数据进行单独空间的存储,节省空间。没有必要每一个对象中都存储一份。可以直接被类名调用。
  • 弊端:生命周期过长。访问出现局限性。(静态虽好,只能访问静态。)
class Person
{
String name;//成员变量,实例变量。
static String country = "CN";//静态的成员变量,类变量。
public static void show()
{
System.out.println("::::");
this.haha();
}
public void haha()
{}
}

class StaticDemo
{
public static void main(String[] args)
{
Person p = new Person();
//p.name = "zhangsan";
//p.show();

//System.out.println(p.country);

//System.out.println(Person.country);

Person.show();
}
}


主函数:是一个特殊的函数。作为程序的入口,可以被jvm调用。
主函数的定义:
  • public:代表着该函数访问权限是最大的。
  • static:代表主函数随着类的加载就已经存在了。
  • void:主函数没有具体的返回值。
  • main:不是关键字,但是是一个特殊的单词,可以被jvm识别。
  • String[] arr):函数的参数,参数类型是一个数组,该数组中的元素是字符串。字符串类型的数组。
  • 主函数是固定格式的:jvm识别。jvm在调用主函数时,传入的是new String[0];

静态的应用:
  • 每一个应用程序中都有共性的功能,可以将这些功能进行抽取,独立封装。以便复用。
虽然可以通过建立ArrayTool的对象使用这些工具方法,对数组进行操作。发现了问题:
  • 对象是用于封装数据的,可是ArrayTool对象并未封装特有数据。
  • 操作数组的每一个方法都没有用到ArrayTool对象中的特有数据。
  • 这时就考虑,让程序更严谨,是不需要对象的。可以将ArrayTool中的方法都定义成static的。直接通过类名调用即可。
  • 将方法都静态后,可以方便于使用,但是该类还是可以被其他程序建立对象的。为了更为严谨,强制让该类不能建立对象。可以通过将构造函数私有化完成。
  • 接下来,将ArrayTool.class文件发送给其他人,其他人只要将该文件设置到classpath路径下,就可以使用该工具类。
  • 但是,很遗憾,该类中到底定义了多少个方法,对方去不清楚。因为该类并没有使用说明书。
  • 开始制作程序的说明书。java的说明书通过文档注释来完成。

什么时候使用静态?
要从两方面下手:
  • 因为静态修饰的内容有成员变量和函数。
什么时候定义静态变量(类变量)呢?
  • 当对象中出现共享数据时,该数据被静态所修饰。
  • 对象中的特有数据要定义成非静态存在于堆内存中。
什么时候定义静态函数呢?
  • 当功能内部没有访问到肺静态数据(对象的特有数据),那么该功能可以定义成静态的。

Person p = new Person("zhangsan",20);该句话都做了什么事情?
  • 1,因为new用到了Person.class.所以会先找到Person.class文件并加载到内存中。
  • 2,执行该类中的static代码块,如果有的话,给Person.class类进行初始化。
  • 3,在堆内存中开辟空间,分配内存地址。
  • 4,在堆内存中建立对象的特有属性。并进行默认初始化。
  • 5,对属性进行显示初始化。
  • 6,对对象进行构造代码块初始化。
  • 7,对对象进行对应的构造函数初始化。
  • 8,将内存地址付给栈内存中的p变量。

单例模式:
想要保证对象唯一。
  • 为了避免其他程序过多建立该类对象。先禁止其他程序建立该类对象
  • 还为了让其他程序可以访问到该类对象,只好在本类中,自定义一个对象。
  • 为了方便其他程序对自定义对象的访问,可以对外提供一些访问方式。
这三部怎么用代码体现呢?
  • 将构造函数私有化。
  • 在类中创建一个本类对象。
  • 提供一个方法可以获取到该对象
对于事物该怎么描述,还怎么描述。当需要将该事物的对象保证在内存中唯一时,就将以上的三步加上即可。
饿汉式:先初始化对象
class Student
{
private int age;


private static Student s = new Student();
private Student(){}
public static Student getStudent()
{
return s;
}

public void setAge(int age)
{
this.age = age;
}
public int getAge()
{
return age;
}
}

懒汉式:对象是方法被调用时,才初始化,也叫做对象的延时加载
class Single
{
private static Single s = null;
private Single(){}
public static Single getInstance()
{
if(s==null)
{
synchronized(Single.class)
{
if(s==null)
s = new Single();
}
}
return s;
}
}


继承:
  • 1,提高了代码的复用性。
  • 2,让类与类之间产生了关系。有了这个关系,才有了多态的特性。
注意:千万不要为了获取其他类的功能,简化代码而继承。
必须是类与类之间有所属关系才可以继承。所属关系 is a


  • Java语言中:java只支持单继承,不支持多继承。
  • 因为多继承容易带来安全隐患:当多个父类中定义了相同功能,
  • 当功能内容不同时,子类对象不确定要运行哪一个。
  • 但是java保留这种机制。并用另一种体现形式来完成表示。多实现。
  • java支持多层继承。也就是一个继承体系
如何使用一个继承体系中的功能呢?
  • 想要使用体系,先查阅体系父类的描述,因为父类中定义的是该体系*性功能。
  • 通过了解共性功能,就可以知道该体系的基本功能。
  • 那么这个体系已经可以基本使用了。
  • 那么在具体调用时,要创建最子类的对象,为什么呢?
  • 一是因为有可能父类不能创建对象,
  • 二是创建子类对象可以使用更多的功能,包括基本的也包括特有的。
  • 简单一句话:查阅父类功能,创建子类对象使用功能。
class Person
{
String name;
int age;

}
class Student extends Person
{
void study()
{
System.out.println("good study");
}
}

class Worker extends Person
{
void work()
{
System.out.println("good work");
}
}

class ExtendsDemo
{
public static void main(String[] args)
{
Student s = new Student();
s.name = "zhagnsan";
}
}

子父类出现后,类成员的特点:
类中成员:
  • 1,变量。
  • 2,函数。
  • 3,构造函数。
变量
  • 如果子类中出现非私有的同名成员变量时,
  • 子类要访问本类中的变量,用this
  • 子类要访问父类中的同名变量,用super。
  • super的使用和this的使用几乎一致。
  • this代表的是本类对象的引用。
  • super代表的是父类对象的引用。
函数,子父类中的函数。
  • 当子类出现和父类一模一样的函数时,
  • 当子类对象调用该函数,会运行子类函数的内容。
  • 如同父类的函数被覆盖一样。
  • 这种情况是函数的另一个特性:重写(覆盖)
  • 当子类继承父类,沿袭了父类的功能,到子类中,
  • 但是子类虽具备该功能,但是功能的内容却和父类不一致,
  • 这时,没有必要定义新功能,而是使用覆盖特殊,保留父类的功能定义,并重写功能内容
覆盖:
  • 子类覆盖父类,必须保证子类权限大于等于父类权限,才可以覆盖,否则编译失败。
  • 静态只能覆盖静态。
  • 重载:只看同名函数的参数列表。
  • 重写:子父类方法要一模一样。
子父类中的构造函数
  • 在对子类对象进行初始化时,父类的构造函数也会运行,
  • 那是因为子类的构造函数默认第一行有一条隐式的语句 super();
  • super():会访问父类中空参数的构造函数。而且子类中所有的构造函数默认第一行都是super();
为什么子类一定要访问父类中的构造函数。
  • 因为父类中的数据子类可以直接获取。所以子类对象在建立时,需要先查看父类是如何对这些数据进行初始化的。
  • 所以子类在对象初始化时,要先访问一下父类中的构造函数。
  • 如果要访问父类中指定的构造函数,可以通过手动定义super语句的方式来指定。
  • 注意:super语句一定定义在子类构造函数的第一行。
子类的实例化过程。
结论:
  • 子类的所有的构造函数,默认都会访问父类中空参数的构造函数。
  • 因为子类每一个构造函数内的第一行都有一句隐式super();
  • 当父类中没有空参数的构造函数时,子类必须手动通过super语句形式来指定要访问父类中的构造函数。
  • 当然:子类的构造函数第一行也可以手动指定this语句来访问本类中的构造函数。
  • 子类中至少会有一个构造函数会访问父类中的构造函数。
class Fu //extends Object
{
int num ;
Fu()
{
//super();
num= 60;
System.out.println("fu run");
}
Fu(int x)
{
System.out.println("fu ...."+x);
}

}

class Zi extends Fu
{
Zi()
{

super();
//super(4);
System.out.println("zi run");
}
Zi(int x)
{
this();
//super();
//super(3);
System.out.println("zi..."+x);
}
}

class ExtendsDemo4
{
public static void main(String[] args)
{
Zi z = new Zi(0);
System.out.println(z.num);
}
}

final : 最终。作为一个修饰符
  • 可以修饰类,函数,变量。
  • 被final修饰的类不可以被继承。为了避免被继承,被子类复写功能。
  • 被final修饰的方法不可以被复写。
  • 被final修饰的变量是一个常量只能赋值一次,既可以修饰成员变量,有可以修饰局部变量。
  • 当在描述事物时,一些数据的出现值是固定的,那么这时为了增强阅读性,都给这些值起个名字。方便于阅读。而这个值不需要改变,所以加上final修饰。作为常量:常量的书写规范所有字母都大写,如果由多个单词组成。单词间通过_连接。
  • 内部类定义在类中的局部位置上是,只能访问该局部被final修饰的局部变量。
class Demo
{
final int x = 3;
public static final double PI = 3.14;
final void show1()
{}
void show2()
{
final int y = 4;
System.out.println(3.14);
}
}
class SubDemo extends Demo
{
//void show1(){}
}
class FinalDemo
{
public static void main(String[] args)
{
System.out.println("Hello World!");
}
}

接口:可以认为是一个特殊的抽象类当抽象类中的方法都是抽象的,那么该类可以通过接口的形式来表示。
  • class用于定义类
  • interface 用于定义接口。
接口定义时,格式特点:
  • 接口中常见定义:常量,抽象方法。
  • 接口中的成员都有固定修饰符。
  • 常量:public static final
  • 方法:public abstract 
  • 记住:接口中的成员都是public的。
  • 接口:是不可以创建对象的,因为有抽象方法。需要被子类实现,子类对接口中的抽象方法全都覆盖后,子类才可以实例化。否则子类是一个抽象类。
  • 接口可以被类多实现,也是对多继承不支持的转换形式。java支持多实现。
当多个类中出现相同功能,但是功能主体不同,
这是可以进行向上抽取。这时,只抽取功能定义,而不抽取功能主体。

应用特点:
  • 接口是对外暴露的规则。
  • 接口是功能的扩展。
  • 接口的出现降低了耦合性。


抽象类的特点:
  • 抽象方法一定在抽象类中。
  • 抽象方法和抽象类都必须被abstract关键字修饰。
  • 抽象类不可以用new创建对象。因为调用抽象方法没意义。
  • 抽象类中的抽象方法要被使用,必须由子类复写起所有的抽象方法后,建立子类对象调用。如果子类只覆盖了部分抽象方法,那么该子类还是一个抽象类。

抽象类和一般类没有太大的不同。
  • 该如何描述事物,就如何描述事物,只不过,该事物出现了一些看不懂的东西。
  • 这些不确定的部分,也是该事物的功能,需要明确出现。但是无法定义主体。通过抽象方法来表示。
  • 抽象类比一般类多个了抽象函数。就是在类中可以定义抽象方法。
  • 抽象类不可以实例化。
  • 特殊:抽象类中可以不定义抽象方法,这样做仅仅是不让该类建立对象。
  • abstract 关键字,和哪些关键字不能共存。
  • final:被final修饰的类不能有子类。而被abstract修饰的类一定是一个父类。
  • private: 抽象类中的私有的抽象方法,不被子类所知,就无法被复写。而抽象方法出现的就是需要被复写。
  • static:如果static可以修饰抽象方法,那么连对象都省了,直接类名调用就可以了。可是抽象方法运行没意义。
抽象类中是否有构造函数?
有,抽象类是一个父类,要给子类提供实例的初始化。


抽象类和接口异同:
相同:
  • 都可以在内部定义抽象方法。
  • 通常都在顶层。
  • 都不可以实例化,都需要子类来实现。
不同点:
  • 抽象类中可以定义抽象方法和非抽象方法,而接口中只能定义抽象方法。
  • 接口的出现可以多实现。抽象类只能单继承。也就是说:接口的出现避免了单继承的局限性。
  • 继承和实现的关系不一致。继承:is a,实现:like a 

内部类的访问规则:
  • 内部类可以直接访问外部类中的成员,包括私有。之所以可以直接访问外部类中的成员,是因为内部类中持有了一个外部类的引用,格式 外部类名.this
  • 外部类要访问内部类,必须建立内部类对象。
访问格式:
1,当内部类定义在外部类的成员位置上,而且非私有,可以在外部其他类中。
可以直接建立内部类对象。
格式
外部类名.内部类名  变量名 = 外部类对象.内部类对象;
Outer.Inner in = new Outer().new Inner();


2,当内部类在成员位置上,就可以被成员修饰符所修饰。
比如,private:将内部类在外部类中进行封装。
static:内部类就具备static的特性。
当内部类被static修饰后,只能直接访问外部类中的static成员。出现了访问局限。

在外部其他类中,如何直接访问static内部类的非静态成员呢?
new Outer.Inner().function();

在外部其他类中,如何直接访问static内部类的静态成员呢?
uter.Inner.function();

注意:当内部类中定义了静态成员,该内部类必须是static的。
    当外部类中的静态方法访问内部类时,内部类也必须是static的。

当描述事物时,事物的内部还有事物,该事物用内部类来描述。因为内部事务在使用外部事物的内容。

部类定义在局部时
  • 不可以被成员修饰符修饰
  • 可以直接访问外部类中的成员,因为还持有外部类中的引用。但是不可以访问它所在的局部中的变量。只能访问被final修饰的局部变量。
匿名内部类:
  • 1,匿名内部类其实就是内部类的简写格式。
  • 2,定义匿名内部类的前提:内部类必须是继承一个类或者实现接口。
  • 3,匿名内部类的格式:  new 父类或者接口(){定义子类的内容}
  • 4,其实匿名内部类就是一个匿名子类对象。而且这个对象有点胖。 可以理解为带内容的对象。
  • 5,匿名内部类中定义的方法最好不要超过3个。
abstract class AbsDemo
{
abstract void show();

}

class Outer
{
int x = 3;

public void function()
{

AbsDemo d = new AbsDemo()
{
int num = 9;
void show()
{
System.out.println("num==="+num);
}
void abc()
{
System.out.println("haha");
}
};

d.show();
}
}


class InnerClassDemo4
{
public static void main(String[] args)
{
new Outer().function();
}
}