Java面向对象——封装

欢迎光临新长城博客中心


面向对象

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

面向对象的概念:

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

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

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

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

面向对象和面向过程:

图例:

Java面向对象——封装_第1张图片


面向对象特点

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);

}

}

public 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测试类

public 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);

}

}

public class Test{

public static void main(String[] args){

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

p.speak();

}

public static void show(){

System.out.println("cry");

}

}

构造函数内存图解:Java面向对象——封装_第2张图片


this关键字

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

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

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

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

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


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

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

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


this的原理图解:



注意: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呢?

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

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

要从两方面下手:因为静态修饰的内容有成员变量和函数

什么时候定义静态变量(类变量)呢?

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

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

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

当功能内部没有访问到非静态数据,或者对象的特有数据,那么该功能可以定义成静态的。

使用注意

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

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

静态有利有弊

利处:对对象共享的数据进行单独空间的存储,没有必要每个对象都存一份。

可以被直接被类名.调用。

弊端:生命周期过长,访问出现局限性(静态虽好,只能访问静态)

主函数是静态的

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 = arr[0];

for(int x=1; x{

if(arr[x]>max)

max = arr[x];

}

return max;

}

/**

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

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

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

*/

public static int getMin(int[] arr){

int min = 0;

for(int x=1; x{

if(arr[x]

min = x;

}

return arr[min];

}

/**

给int数组进行选择排序。

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

*/

public static void selectSort(int[] arr){

for (int x=0; x{

for(int y=x+1; 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{

for(int y=0; 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{

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 voidmain(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;

}

// 进行name显示初始化值,等同于设置name

public void setName(String 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);

}

}

public 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(){}

//在类中创建一个本类对象(final关键字等下有学到,是最终的意思,呵呵)

private finla static Single single =new Single();

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

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;

}

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

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

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

总结:

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

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

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

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

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



你可能感兴趣的:(Java基础)