黑马程序员 Java面向对象——封装

时间:2023-02-13 23:58:26

---------------------- ASP.Net+Unity开发.Net培训、期待与您交流! ----------------------


面向对象

在Java中,高手们的概念就是万物皆对象。

面向对象的概念:

面向对象:是基于面向过程的一种思想。

面向过程:强调的是功能行为。

面向对象:将功能封装进对象,强调具备了功能的对象。

面向对象是基于面向过程的。

面向对象和面向过程:

图例:

黑马程序员 Java面向对象——封装

面向对象特点

1,面向对象就是一种常见的思想。符合人们的思考习惯。

2,面向对象的出现,将复杂的问题简单化。

3,面向对象的出现,让曾经在过程中的执行者,变成了对象中的指挥者。

面试题:你怎么理解面向对象的?

1,  它符合现在人们思考的一种习惯

2,  它让我们复杂的事情简单化

3,  让我们从曾经的执行都变为现在的指挥者

其实面试官你本身就在用面向对象的方式思考问题

因为以面试官您的能力而言,来了一个软件项目的话,您从需求分析到设计到开发到测试,都能完成,但是这样特别耗时间,所以您为了提高效率,您就需要

去找一些具备专业编程经验的人来完成这些项目,我正好就是那个具备专业编程经验的对象,您只要指挥我这个对象做事情就可以了,我会给您一个非常满意的

结果,至于过程您不用管。所以面试官您就在用面向对象的方式思考问题,来提高公司的效率,而我就是具备专业编程经验的人。

 面向对象有三个特征:封装,继承,多态

以后的开发过程:其实就是找对象用。没有对象,就创建一个对象。

找对象,建立对象,使用对象,并维护对象的关系。

类和对象的关系

类:就是对现实生活中事物的描述。

对象:就是这类事物,实实在在存在的个体。

想要描述:提取对象*性内容。对具体的抽象。

映射到Java中描述就是class定义的类。

具体对象就是对应Java在堆内存中用new建立实体。

例子:

需求:描述小汽车。描述事物其实就是在描述事情的属性和行为。

分析:

1,属性:轮胎数。颜色。

2,行为: 运行。

定义类其实就是在定义类中的成员。

成员:成员变量<-->属性,成员函数<-->行为。

属性对应是类中变量,行为对应的类中函数或方法。

其实定义类,就是在描述事物,就是在定义属性和方法,属性和行为共同成为类中的成员(成员变量和成员方法)。

类中不必有主函数,不必保证独立运行,只有保证有一个主函数入口调用类就行。

//描述汽车类

class Car{

//描述汽车的颜色

String color ="red";

//描述汽车的轮胎数

int num=4;

//描述车的运行

void run(){

System.out.println("color="+color+"..."+"num="+num);

}

}

class CarTest{

public static void main(String[] args){

//生成汽车,在Java中通过new操作符来完成

//其实就是在堆内存产生一个实体

Car c =new Car();//为什么要加括号呢?

//c就是一个类类型变量记住:类类型变量指向对象

//需求:将已有车的颜色改成蓝色,指挥该对象做使用,在Java中指挥方式是:对象.对象成员

c.color = "bule";

c.run();//color=bule...num=4

new Car().color="green";//匿名对象,不过调用匿名对象的属性没有意义。

new Car().run();//匿名对象调用方法,只调用一次。打印的结果是color=red...num=4

method(new Car());  //可以将匿名对象作为实际参数进行传递

}

//需求:汽车修配厂,对汽车进行改装,将来的车都改成黑色,三个轮胎。

public static void method(Car c){

//将汽车的颜色喷漆成黑色

c.color="black";

//将汽车的轮胎改成3个

c.num = 3;

c.run():

}

}

匿名对象,没有名字的对象 。

new Car();//匿名对象。其实就是对象的简写格式。

1,  当对象对方法仅进行一次调用的时候,就可以简化成匿名对象。

2,  匿名对象可以作为实际参数进行传递。

注意:调用匿名对象的属性是没有意义。

成员变量和局部变量的区别

1,

成员变量定义在类中,作用于整个类中。

局部变量定义在函数,语句,局部代码块中,只在所属的大括号区域有效。 

2,

成员变量存在于堆内存的对象中。

局部变量存在于栈内存的方法中。 

3,

成员变量随着对象的创建而存在,随着对象的消失而消失。

局部变量随着所属区域的执行而存在,随着所属区域的结束而释放。 

4,

成员变量都有默认初始化值。

局部变量没有默认初始化值。

面向对象——封装(Encapsulation)

封装:是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。

封装好处:

1.将变化隔离;

2.便于使用。

3.提高重用性。

4.提高安全性。

封装原则:

将不需要对外提供的内容都隐藏起来。

把属性都隐藏,提供公共方法对其访问。

private:关键字

A:用于修饰成员变量和成员方法。
B:被修饰的内容在其他类中是不可以被访问的。

注意:私有仅仅是封装的一种体现而已。

/*

Person类。对Person的年龄进行私有化,让外界不能直接访问,对外提供公共的访问方式。

*/

class Person{

//成员变量不赋值也可以进行运行,默认初始化值是null

String name;

//私有化年龄

private int age;

//对外提供公共设置的访问方式

public void setAge(int a){

//加入判断,使程序更为符合逻辑

if(a>=0 && a<=120)

age=a;

else

System.out.println("年龄非法");

}

//对外提供公共的获取方法

public int getAge(){

return age;

}

//人可以说出自己的名字和年龄

public void speak(){

System.out.println("name="+name+"...age="+age);

}

}

//Person测试类

class PersonTest(){

public static void main(String[] args){

Person p = new Person();

//对p对象的name属性进行赋值。

//p.age=-20//age被直接访问有安全隐患,有非法数据,加关键字private,age默认值是0

//p.setAge(-20);失败,因为不符合Person类中年龄的设置规则。

p.name="Xcc";

//对p对象的age进行设置。

p.setAge(20);

//调用p对象的speak方法,打印自己的姓名和年龄。

p.speak();

}

}

构造函数:

构造函数特点

1.    函数名与类名相同

2.    不用定义返回值类型

3.    没有具体的返回值。

构造函数:构建创造对象时调用的函数。

构造函数作用:给对象进行初始化。

创建对象都必须要通过构造函数初始化。

一个类中如果没有定义过构造函数,那么该类中会有一个默认的空参数构造函数。

如果在类中定义了指定的构造函数,那么类中的默认构造函数就没有了。

注意

1. 默认构造函数的特点。
2. 多个构造函数是以重载的形式在在的。

一般函数和构造函数什么区别呢?

构造函数:对象创建时,就会调用与之对应的构造函数,给对象进行默认初始化。

一般函数:对象创建后,需要函数功能时才调用。(函数只有被调用才执行)

构造函数:对象创建时,只调用一次。

一般函数:对象创建后,可以被调用多次。

什么时候定义构造函数呢?

在描述事物时,该事物已存在就具备一些内容,这些内容都定义在构造函数中。

构造函数可以有多个,用于对不同的对象进行针对性的初始化。

多个构造函数在类中是以重载的形式来体现的。

构造函数的小细节

当一个类中没有定义构造函数时,系统会默认给该类加入一个空参数的构造函数Person(){}

当在类中自定义了构造函数后,默认的构造函数就没有了

class Person{

String  name;

int age;

Person(){}

Person(String name,int age){

/*这里先穿越时空一下,等下会说到this关键字*/

this.name=name;

this.age=age;

}

public void speak(){

System.out.println("name="+name+"...age="+age);

}

}

class Person{

public static void main(String[] args){

Person p2 = new Person("小强",10);

p.speak();

}

public static void show(){

System.out.println("cry");

}

}


构造函数内存图解:

黑马程序员 Java面向对象——封装

黑马程序员 Java面向对象——封装

this:关键字

特点:  this代表其所在函数所属对象的引用。

换言之:this代本类对象的引用。

什么时候使用this关键字呢?

当在函数内需要用到调用该函数的对象时,就用this

当成员变量和局部变量重名,可以用关键字this来区分。

this代表对象。代表哪个对象呢?当前对象。

this:就是所在函数所属对象的引用。

简单说:哪个对象调用了this所在的函数,this就代表哪个对象。

this的原理图解:

黑马程序员 Java面向对象——封装


注意:this也可以用于在构造函数中调用其他构造函数。

注意this只能定义在构造函数的第一行。因为初始化动作要先执行。

注意构造函数间调用只能用this。

/*
 需求:给人定义一个用于比较年龄是否相同的功能,也就是是否是同龄人
 */

super关键字

如果要在函数内访问类中的同名成员变量,用super进行区分。

注意:每个类身上都是一个隐式的super();那么这个空参数的super构造函数是哪个呢?查阅API发现是Object,他是所有类的父类。

构造代码块

构造函数块作用:给所有对象进行初始化。 

对象一建立就运行,而且优先于构造函数执行和构造函数的区别:

构造代码块是给所有对象进行统一初始化。

而构造函数是给对应的对象进行初始化。

构造代码块中定义的是不同对象共性的初始化内容。

总结:构造代码块和构造函数共性的地方就是当对象创建时,就给对象进行初始化一次。


class Person{

{

System.out.println("欢迎,我是构造代码块!");

}

//考虑到每个对象都是需要一个国籍,所以把国籍定义为共享对象。(等下会学到哦)

private static String contry = "cn";

private int age;

Person(int  age){

this.age=age;

}

private boolean compare(Person p){

return this.age==p.age;

}

public void speak(Person p){

boolean b = compare(p)

System.out.println("年龄:"+b+"...国籍:"+contry);

}

}

class CompareAge{

public static void main(String[] args){

Person p1 = new Person(20);

Person p2 = new Person(21);

p1.speak(p2);

}

}


static关键字

用于修饰成员(成员变量和成员函数)

被修饰后的成员具备以下特点:

1.随着类的加载而加载

2.优先于对象存在

3.被所有对象所共享

4.可以直接被类名调用

static特点

1,  static是一个修饰符,用于修饰成员。(成员函数,成员变量)

2,  static修饰的成员被所有的对象所共享。

3,  static优先于对象存在,因为static的成员随着类的加载就已经存在了。

4,  static修饰的成员多了一种调用方式,可以直接被类名调用。类名.静态成员。

5,  static修饰的数据是共享数据,对象中存储的是特有数据。

成员变量和静态变量的区别?

1,  两个变量的生命周期不同。

成员变量随着对象的创建而存在,随着对象的被回收而释放。

静态变量随着类的加载而存在,随着类的消失而消失。

2,  调用方式不同。

成员变量只能被对象调用。

静态变量可以被对象调用,还可以被类名.调用。

3,  别名不同。

成员变量也称为实例变量。

静态变量称为类变量。

4,  数据存储位置不同。

成员变量存储在堆内存的对象中,所以也叫对象的特有数据。

静态变量数据存储在方法区(共享数据区)的静态区,所以也叫对象的共享数据。


什么时候该用static呢?

如果某个内容是所有对象共享的,就用静态修饰。

什么时候使用静态成员和函数呢?

要从两方面下手:
因为静态修饰的内容有成员变量和函数
什么时候定义静态变量(类变量)呢?

当对象中出现可共享的数据时,该数据被静态修饰。

对象中的特有数据要定义成非静态存在于堆内存中。

什么时候定义静态函数呢?

当功能内部没有访问到非静态数据,或者对象的特有数据,

那么该功能可以定义成静态的


使用注意

1.静态方法可能访问静态成员类名.静态成员。

2.静态方法中不可以写thissuper关键字,因为静态先于对象操作

静态有利有弊
利处:对对象共享的数据进行单独空间的存储,没有必要每个对象都存一份
可以被直接被类名调用
弊端:生命周期过长,访问出现局限性(静态虽好,只能访问静态)

3.主函数是静态的

public static void main(String[] args)

主函数特殊之处

1,  格式是固定的。(除了args这个数组名字可以变,其他不行)

2,  被Jvm所识别和调用。

public:因为权限必须是最大的。

static:不需要对象的,直接用主函数所属类名调用即可。

void:主函数没有具体的返回值。(因为返一个值给虚拟机,虚拟机也害怕啊!是不是虚拟机没有内存存放)

main:函数名,不是关键字,只是一个Jvm识别的固定的名字。

String[] args:这是主函数的参数列表,是一个数组类型的参数,而且元素都是字符串类型。

public static void main(String[] args){

System.out.println(args);//打印[Ljava.lang.String;@2536716a,当然大家的内存地址值都是不一样的。

}

什么时候定义静态函数?

当功能内部没有访问到非静态数据(对象的特有数据),那么该功能可以定义成静态的。(说了也不清楚,下面来一个静态的应用)

静态的应用

/**     

*类描述

*@author 作者名

*@version 版本号

*/

/**

*方法描述

*@param  参数描述

*@return  返回值描述

*/

/**

这是一个可以对数组进行操作的工具类,该类中提供了,获取最值,排序等功能。

@author 黑马程序员——Xcc

@version V1.1

*/

//类名必须被public修饰,才会生成帮助文档。被private修饰的不会被暴露。

public class ArrayTool{

/*

一个类中默认会有一个空参数的构造函数,

这个默认的构造函数的权限和所属类一致。

如果类被public修饰,那么默认的构造函数也带public修饰符。

如果类没有被public修饰,那么默认的构造函数,也没有public修饰。

默认构造构造函数的权限是随着的类的变化而变化的。

当然我们也可以自己设置指定构造函数的权限。

*/

/*

为什么构造函数是私有的呢?

因为本类没有涉及到特有数据。类中的方法都是静态的,都可以用类名.的方式访问。

为了严谨,把构造函数私有化,不让该类创建对象。

*/

/**

空参数私有构造函数

*/


private ArrayTool(){}

/**

获取一个整形数组中的最大值。

@param arr 接收一个int类型的数组。

@return 会返回一个该数组中最大值。

*/

public static int getMax(int[] arr){

int max = 0;

for(int x=1; x<arr.length; x++){

if(arr[x]>arr[max])

max = x;

}

return arr[max];

}

/**

获取一个整形数组中的最小值。

@param arr 接收一个int类型的数组。

@return 会返回一个该数组中最小值。

*/

public static int getMin(int[] arr){

int min = 0;

for(int x=1; x<arr.length; x++){

if(arr[x]<arr[min])

min = x;

}

return arr[min];

}

/**

给int数组进行选择排序。

@param arr 接收一个int类型的数组。

*/

public static void selectSort(int[] arr){

for (int x=0; x<arr.length-1 ; x++ ){

for(int y=x+1; y<arr.length; y++){

if(arr[x]>arr[y]){

swap(arr,x,y);

}

}

}

}

/**

给int数组进行冒泡排序。

@param arr 接收一个int类型的数组。

*/

public static void bubbleSort(int[] arr){

for (int x=0; x<arr.length-1 ; x++ ){

for(int y=0; y<arr.length-x-1; y++){

if(arr[y]>arr[y+1]){

swap(arr,y,y+1);

}

}

}

}

/**

给数组中元素进行位置的置换。

@param arr  接收一个int类型的数组。

@param a 要置换的位置 

@param b 要置换的位置 

*/

private  static void swap(int[] arr,int a,int b){

int temp = arr[a];

arr[a] = arr[b];

arr[b] = temp;

}

/**

用于打印数组中的元素。打印形式是:[elemet1, element2, ...]

*/

public static void printArray(int[] arr){

System.out.print("[");

for(int x=0; x<arr.length; x++)

{

if(x!=arr.length-1)

System.out.print(arr[x]+", ");

else

System.out.println(arr[x]+"]");

}

}

}

/*

每一个应用程序中都有共性的功能,可以将这些功能进行抽取,独立封装,以便复用。

虽然可以通过建立ArrayTool的对象使用这些工具方法,对数组进行操作,但发现了问题:

对象是用于封装数据的,可是ArrayTool对象并未封装特有数据;操作数组的每一个方法都没有用到ArrayTool对象中的特有数据

这是就可虑让程序更加严谨,是不需要对象的,可以将ArrayTool中的方法都定义成static的,直接通过类名调用即可。

将方法都静态后,可以方便于使用,但是该类还是可以被其它程序建立对象的,为了更严谨,强制让该类不能建立对象,可以通过将构造函数私有化来完成。


接下来,将ArrayTool.class文件发送给其他人,其他人只要将该文件设置到classpath路径下,就可以使用该工具类。
但是,很遗憾,该类中到底定义了多少个方法,对方去不清楚。因为该类并没有使用说明书。
开始制作程序的说明书。Java的说明书通过文档注释来完成。

生成Java帮助文档:Javadoc(目录)-d (文件夹名字)myhelp (作者)-author (版本号)-version (生成注释文档的文件)ArrayTool.java

*/

静态代码块:

格式:

static{

静态代码块中的执行语句

}

特点:随着类的加载而执行,只执行一次,并优先于主函数


对象初始化的顺序:

静态代码块——>静态主函数——>构造代码块——>构造函数

例子:

class Test{

static

{

System.out.println("Static Code");

}


{

System.out.println("Code");

}

Test(){

System.out.println("Test Code");

}

public static void main(String[] args){

System.out.println("Main Code");

new Test();

}


}

/*

private封装

this用法

构造函数

static小应用

对象初始化顺序

*/

class Person {

private String name;

private int age;

private static String country ="cn";

{

System.out.println("cry...");

}

public int getAge() {

return age;

}

public void setAge(intage) {

this.age = age;

}

Person(){}

Person(String name,int age) {

this.name = name;

this.age = age;

}

public void setName(String name)// 进行name显示初始化值,等同于设置name

{

this.name = name;

}

public String getName() {

return name;

}

public void speak() {

System.out.println("name=" + name +"..age=" + age);

show();// 打印当前对象的country,this也被省略

}

public static void show() {

System.out.println("country=" + country);

}

}

class PersonTest {

public static void main(String[] args) {

Person p = new Person("Xcc",19);

p.setName("黑马程序员");

p.setAge(20);

p.speak();

}

}
Person p = new Person("Xcc",19);
这句话都做了什么?
1,因为new用到了Person.class,所以会先找到Person.class文件并加载到内存中
2,执行该类中的static代码块,如果有的话,给Person.class类进行初始化
3,在堆内存中开辟空间,分配内存地址
4。在堆内存中建立对象的特有属性,并进行默认初始化(name=null age=0 country=null)
5。对属性进行显示初始化(也就是属性初始化,如果没有赋值就是默认初始化的值 name=null age=0 country="cn")
6。对对象进行构造代码块初始化
7。对对象进行对应的构造函数初始化

8。将内存地址赋给栈内存中的p变量

设计模式:

解决某一类问题最行之有效的方法,Java中23种设计模式。

单例设计模式(Singleton)解决一个类在内存中只存在一个对象

比如对于多个程序使用同一个配置信息对象时,就需要保证该对象的唯一性。

想要保证对象唯一:

为了避免其他程序过多建立该类对象,先禁止其他程序建立该类对象;

还为了让其他程序可以访问到该类对象,只好在本类中自定义一个对象;

为了方便其他程序对自定义对象的访问, 可以对外提供一些访问方式;

单例设计模式之饿汉式

Single类一进内存,就已经创建好了对象,简单的说就是一上来就吃。

思路:

将构造函数私有化;

在类中创建一个本类对象;

提供一个公共的访问方法,可以获取到该类对象;

步骤:

private Single(){}//将构造函数私有化

private finla static Single single = new Single();//在类中创建一个本类对象(final关键字等下有学到,是最终的意思,呵呵)

public static Single getInstance(){

return single;

}//提供一个公共的访问方法,可以获取到该类对象


单例设计模式之懒汉式

对象是方法被调用时才初始化,也叫对象的延时加载

Single类进内存,对象还没有存在,只有调用了getInstance方法时,才建立对象。

步骤:

private Single(){}

private static Single single =null;

public static Single getInstance(){

if(single==null){

Synchronized(Single.class)

{

if(single==null)

single =new Single();

}

}

return single;

}

为什么有饿汉式还会有懒汉式?

原因就是对了面试,因为面试都是考懒汉式,因为要考虑到了多线程安全问题,使程序更加严谨。

实际开发中用饿汉式,因为在考虑多线程时会比较安全,懒汉式的解决安全问题的方法,双重判断,加入锁。

总结:

饿汉式:一上来就对对象初始化。

浪费一点点内存,因为不调用也执行嘛。

懒汉式:对象调用方法时,才初始化,也叫做对象的延时加载。

一点点内存都省,因为只有调用才占内存嘛。

习惯成自然,养成良好的节约内存习惯,对日后开发还有很有帮助滴。我不知道大家是不是这样,反正我是这样了。


---------------------- ASP.Net+Unity开发.Net培训、期待与您交流! ----------------------