Java 面向对象(封装、继承、多态+面向对象、构造函数、静态代码块)

时间:2021-08-30 19:29:42

Java面向对象

面向对象概念

类与对象的关系

封装

构造函数

this关键字

static关键字

单例设计模式

1.       面向对象概念

1.1    理解面向对象

面向对象是相对面向过程而言。

面向对象和面向过程都是一种思想。

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

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

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

eg

Java 面向对象(封装、继承、多态+面向对象、构造函数、静态代码块)

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

面试可能会问什么是面向对象?

  面向对象是一种思想(说特点)能让复杂问题简单化,从角度上发生改变从执行者变成指挥者。强调结果

  举个例子(实际场景)

写程序,先找对象,没有对象造一个对象拿来用。

eg:

人开门:名词提炼法

{

            开门()

            {

                     .();

}

}

{

            ()

{

操作门轴等;

}

}

1.1    面向对象的特点

面向对象三个特征:1>封装、2>继承、3>多态。

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

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

类和对象的关系

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

对象:就是这类事物,实实在在的存在个体(new操作符产生的实体)

eg

现实生活中的对象:张三、李四

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

描述时:这些对象的共性有:姓名、年龄、性别、学习java功能

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

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

1.       类与对象的关系

2.1 类与对象(图例)

类与对象的关系(如图)

Java 面向对象(封装、继承、多态+面向对象、构造函数、静态代码块)

可以理解为:

类就是图纸

汽车就是对内存中的对象

eg:描述汽车(颜色、轮胎数),描述事物其实就是在描述事物的属性和行为。

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

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

(成员变量和成员方法)

class Car
{
//描述颜色
String color = “红色”;
//描述轮胎数
int num=4;
//运行行为
void run()
{
System.out.println(color+”…”+num);
}
}
class CarDemo
{
public static void main(String[] args)
{
//生产汽车,在java中通过new操作符来完成。
//其实就是在堆内存中产生一个实体
Car c = new Car(); //c就是一个类类型变量,记住:类类型变量指向对象。

//需求:将已有的车的颜色改成蓝色。指挥该对象做事情。在java指挥方式是:
//对象.对象成员
c.color = “blue”;
c.run();
Car c1=new Car();
c1.run();
}
}


 

2.2 匿名对象

匿名对象是对象的简化形式

匿名对象两种使用情况:

1>     当对对象方法仅进行一次调用时(如果多个成员调用,必须给这个对象起个名)

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

eg:

Car c = new Car();
c.num = 5;
//匿名写法:
new Car().num = 5;
eg:
new Car().num = 5;
new Car().color=”blue”;
new Car().run();
//这三句话是这样的(如图):


Java 面向对象(封装、继承、多态+面向对象、构造函数、静态代码块)

eg:

main()
{
Car c = new Car();
show(c);
//以上两句代码可以写成如下格式:
//show(new Car());
}
public static void show(Car c)
{
c.num = 3;
c.color = “black”;
c.run();
}


Java 面向对象(封装、继承、多态+面向对象、构造函数、静态代码块)

3.       封装

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

好处:

将变化隔离;

便于使用;

提高复用性;

提高安全性。

         封装原则:

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

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

eg

         private:私有,权限修饰符;用于修饰类中的成员(成员变量、成员函数)。

                           私有只在本类中有效。

class Person
{
private int age;
void speak()
{
System.out.println(“age=”+age);
}
}
class PersonDemo
{
public static void main(String[] args)
{
Person p = new Person();
}
}


 

/*
将age私有化以后,类以外即使建立了对象也不能直接访问。但是人应该有年龄,就需要在Person类中提供对应访问age的方式。
*/
一个成员变量通常会对应两个访问方式,1、设置;2、获取
public void setAge(int a)
{
age = a;
}
public int getAge()
{
return age;
}


 

注意:私有仅仅是封装的一种表现形式。

之所以对外提供访问方式,就因为可以在访问时加入逻辑判断等语句。对访问的数据进行操作。提高代码健壮性。

4.       构造函数

特点:

1、  名字与类名相同

2、  不用定义返回值类型(但可以用publicprivateprotected修饰)

3、  不可以写return语句

作用:

给对象进行初始化(对象一建立,就会调用与之对应的构造函数)

注意:

1、  默认构造函数的特点。

2、  多个构造函数是以重载的形式存在的。

小细节:

当一个类中没有定义构造函数时,那么系统会默认给该类加入一个空参数的构造函数。方便给该类初始化,否则对象无法建立。如果自己定义了,则系统不再建立默认构造函数。一个默认的构造函数的权限和所属类一致。

1、  构造函数和一般函数在写法上有不同。

2、  在运行上也有不同:

构造函数一建立就运行,给对象初始化。而一般函数是对象调用才执行,是给对象添加对象具备的功能。一个对象建立,构造函数只运行一次而一般方法可以被该对象调用多次。

eg:

class Person
{
Person()
{
System.out.println(“person run”);
}
}
class PersonDemo2
{
public static void main(String[] args)
{
Person p = new Person();
new Person();
//以上两句都会执行:System.out.println(“Person run”);
}
}
//如果在Person类中添加这么介个构造函数
Person()
{
System.out.println(“A:name=”+neme+”,age=”+age);
}
Person(String n)
{
name=n;
System.out.println(“B:name=”+name+”,age=”+age);
}
Person(String n,int a)
{
name=n;
age=a;
System.out.println(“C:name=”+name+”,age=”+age);
}
//在class PersonDemo2中执行如下语句
Person p1=new Person();
Person p2=new Person(“lisi”);
Person p3=new Person(“wang”,10);


 

//分别会输出如下

A:name=null,age=0

B:name=lisi,age=0

C:name=wang,age=10

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

当分析事物时,该事物存在具备一些特殊性或者行为,那么将这些内容定义在构造函数中。

构造代码块:

class Person
{
private String name;
private int age;
//构造代码块
{
System.out.println(“HaHa……”);
}
}


 

即直接在{}中写语句,没返回值,名字等。

作用:给对象进行初始化。

对象一建立就运行,而且优先于构造函数执行;

和构造函数的区别:

构造代码块是给所有对象进行初始化(即共性东西),而构造函数是给对应的对象初始化。

//当局部变量与成员变量名称相同时

eg:

class Person
{
private String name;//成员变量
Person(String name)//这里的name为局部变量
{
name=name;//此时这个name是局部变量(局部中有先在自身找,没有了才//去外面找。
}
}
public static void main(String[] args)
{
Person p=new person(“lisi”);//此时name=null;不是lisi
}

这时就需要一个this

上面改成

Person(String name)
{
this.name=name;
}

 

this:就代表本类的对象[this代表它所在函数所属对象的引用]

eg

Person p=new Person(“lisi”);


 

this 代表 pthis.name即为p.name

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

this的应用:当定义类中功能时,该函数内部要用到调用该函数的对象时,这是用this来表示这个对象。

注:构造函数间调用,用:this(参数名)this语句。且必须放在第一行,因为初始化要先执行。切忌出现死循环,即两个函数一直相互调用。

eg

Person(String name)
{
this.name=name;
}
Person(String name;int age)
{
this(name);
this.age=age;
}


 

5.      static(静态)关键字

static关键字

用于修饰成员(成员变量和成员函数),不可修饰局部

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

1>     随着类的加载而加载,消失而消失。说明它的生命周期最长。

2>     优先于对象存在

3>     被所有对象所共享

4>     可以直接被类名调用【类名.静态成员】

使用注意:

1>     静态方法只能访问静态成员(方法+变量)【非静态方法既可以访问静态,也可以访问非静态】

2>     静态方法中不可以写thissuper关键字(因为静态优先于对象存在,所以静态方法中不可以出现this

3>     主函数是静态的

eg

class Person
{
String name;//成员变量,实例变量
static String country =”CN”;//静态成员变量,类变量
public void show()
{
System.out.println(name+”::”+country);
}
}
class StaticDemo
{
public static void main(String[] args)
{
Person p= new Person();
p.name=”zhangsan”;
p.show();
}
}


static修饰后,多一个调用方式,即可以被类调用:Person.country

静态修饰的东西在“方法区”。

实例变量和类变量的区别:

1、  存在位置

类变量随着类的加载而存在于方法区中。

实例变量随着对象的建立而存在于堆内存中。

2、  生命周期

类变量生命周期最长,随着类的消失而消失

实例变量生命周期随着对象的消失而消失

静态有利有弊:

利:对对象的共享数据进行单独空间的存储,节省空间,没必要每一个对象都存储一份。可以直接被类名调用。

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

 

public static void main(String[] args)   //只有args的名称可以换。其它的都不可以换。

解析:

主函数:是一个特殊的函数,作为程序的入口,可以被jvm调用。

主函数的定义:

public:代表该函数的访问权限是最大的。

static:代表主函数随着类的加载就已经存在了。

void:主函数没有具体的返回值。

main:不是关键字,但是是一个特殊的单词,可以被jvm识别。

(String[] args):函数的参数,参数类型是一个数组,该数组中的元素是字符串。字符串类型数组。

主函数是固定格式的:jvm识别

如果打印System.out.println(args);

得出的结果:[Ljava.lang.String;@2a139a55

jvm在调用主函数时,传入的是 new String[0];          //长度为0

如果直接:

public static void main(String[] args)
{
System.out.println(args.length);//长度为0
System.out.println(args[0]);
}


执行:

javac MainDemo.java

java MainDemo【会报错】

java MainDemo haha hehe heihei【手动添加元素】//结果为 haha

什么时候使用静态?

从两方面下手:

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

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

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

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

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

/*静态的应用

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

如建立一个ArrayTool类,把选择最大值,最小值,排序都放进去。对数组进行操作。

发现了问题:

1、  对象是用于封装数据的,可是ArrayTool对象报告并未封装特有数据。

2、  操作数组的每一个方法都没有用到ArrayTool对象中的特有数据。

这时就要考虑,让程序更严谨,是不需要对象的。

可以将ArrayTool中的方法都定义成Static的,直接通过类名调用即可。

将方法都静态后,可以方便于使用。

为了更为严谨,强制让该类不能建立对象。

可以通过将构造函数私有化完成。

*/

6.       帮助文档的制作(说明书,也可称为:“API”帮助文档)

eg:上面地ArrayTool

java类代码文件打开后,写如下内容:

/**

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

@author 张三

@version V1.1

*/

(在获取最大值方法前)

/**

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

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

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

*/

其他方法类比以上。

生成帮助文档,dos下:

当前文件夹, javadoc –d myhelp –author –version ArrayTool.java

-d:存放的目录。

myhelp:放目录myhelp中,前面可加地址。

–author –version:可以不写,如果要这个内容,得写上。

ArrayTool.java:类名

注:1.只可以生成publicprotected修饰的类(否则被视为封装);2.private修饰的方法没有被提取,只有publicprotected可被提取。

7.      静态代码块

格式:

static

{

         静态代码块中的执行语句;

}

特点:随着类的加载而执行,只执行一次。

用于给类进行初始化的。

Java中执行顺序:(优先级从高到低)静态代码块>main方法>构造代码块>构造方法

class StaticCode
{
static
{
System.out.println(“a”);
}
public static void show()
{
System.out.println(“show run”);
}
}
class StaticCodeDemo
{
static
{
System.out.println(“b”);
}
public static void main(Sting[] args)
{
//new StaticCode();
//new StaticCode(); //这句话没有执行,因为上面执行了一次静态代码块
//System.out.println(“over”);
//StaticCode.show();
StaticCode s=null;//只执行这一句,并不执行静态代码块,因没用到类
s=new Static..code();//这样静态代码块会被执行
}
}


8.       设计模式

设计模式:解决某一类问题最行之有效的方法。

java23种设计模式:

单例设计模式:解决一个类在内存中存在一个对象。

 

想要保持对象唯一。

1、  为了避免其他程序过多建立该类对象。先禁止其他程序建立该类对象。

2、  为了让其它程序可以访问到该类对象,只好在本类中自定义一个对象。

3、  为了方便其它程序对自定义对象的访问,也可对外提供一些访问方式。

这三部怎么用代码实现呢?

1.       将构造函数私有化。

2.       在类中创建一个本类对象。

3.       提供一个方法可以获取到该对象。

注:对事物类该怎么描述还怎么描述。只是在需要保持对象唯一时,用以上三步。

eg

//Single的写法一(饿汉式):
class Single
{
private Single(){}
private static Single s=new Single();
public static Single getInstance()
{
return s;
}
}
class SingleDemo
{
public static void main(String[] args)
{
Single ss=Single.getInstance();
}
Single s=Single.getInstance();
}
//以上ss和s是一个对象【如图】。


Java 面向对象(封装、继承、多态+面向对象、构造函数、静态代码块)

//写法二(懒汉式):
//对象是方法被调用时,才初始化,也叫做对象的延时加载。
class Single
{
private static Single s=null;
private Single(){}
public static Single getInstance()
{
if(s==null)
s=new Single();
return s;
}
}
/*
上句中的if语句那里,存在风险。A程序cpu到这里要处理中断,B程序也到这里执行,就重复建立了对象。
改进方法:
Synchronized(同步)上锁,A进去,不执行完,B不会进去
public static synchronized Single getInstance()//会降低效率,涉及判断。
{
……;
}
*/
方法二改进:
public static Single getInstance()
{
if(s==null)
{
Synchronized(Single.class)
{
if(s==null)
s=new Single();
}
}
return s;
}


8.       继承

继承的概述

继承的特点

super关键字

函数涵盖

子类实例化过程

final关键字

1.       继承(is a):

继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。

2.       特点

1.       提高了代码的复用性

2.       让类与类之间产生了关系。有了这个关系,才有了多态的特征。

注意:千万不要为了获取其他类的功能简化代码而继承。必须是类与类之间有所属关系才可以继承。

为了判断二者关系可以让B继承A,然后看看B是否应该具有A中的属性

java语言中:java只支持单继承,不支持多继承(因为容易带来安全隐患,如两个相同的方法……不确定用哪个);但是java保留了这种机制。并用另一种体现形式来完成表示:多实现;java支持多层继承(ABC),也就是一个继承体系。

         如何使用一个继承体系?(一句话:查阅父类功能,创建子类对象)

先查阅体系父类(体系共性),也就了解了基本功能。

在具体调用时,要创建最子类的对象(1.有可能父类不能创建对象,2.创建子类对象可以使用更多的功能,包括基本的,也包括特有的)。

java中还有其他关系(聚集(has a),聚合,组合)

3.      Super

代表父类对象的引用,即:super.变量(与this使用方法基本一致)

变量:

如果子类中出现非私有的同名成员变量时,如:子父中都有num变量,子类要访问用this,子要访问父类numsuper

函数:

子类、父类中的函数:

当子类出现和父类一模一样的函数时,当子类对象调用该函数,会运行子类函数的内容。如同父类的函数被覆盖一样。

这种情况是函数的另一个特性:重写(覆写)【比如程序升级、更新换代】

eg:

class Tel
{
void show()
{
System.out.println(“number”);
}
}
class NewTel extends Tel
{
void show()
{
super.show();
System.out.println(“name”);
System.out.println(“pic”);
}
}

注:

1>     子类覆盖父类,必须保证子类权限大于等于父类权限。

2>     静态只能被静态覆盖。

记住:

重载:只看同名函数的参数列表。(继承中没有这个概念)

重写:子父类方法要一摸一样。

4.       子父类中的构造函数。

在对子类对象进行初始化时,父类的构造函数也会运行,因为子类的构造函数默认第一行有一条隐式的语句:super();

super():会访问父类中空参数的构造函数,而且子类中所有的构造函数第一行都默认super();

为什么子类一定要访问父类中的构造函数?

因为父类中的数据子类可以直接获取,所以子类对象在建立时,需要先查看父类是如何对这些数据进行初始化的。

所以子类在对象初始化时,要先放问一下父类中的构造函数。如果要访问父类中指定的构造函数,可以通过手动定义super语句来指定。

class Fu
{
int num;
Fu()
{
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)
{
//super();
super(3);
System.out.println(“Zi…”+x);
}
}


 

10.       final关键字

final可以修饰类、方法、变量

final修饰的类不可以被继承。(为了避免被继承,被子类覆写)

final修饰的方法不可以被覆盖。

final修饰的变量是一个常量,只能被赋值一次。(例如:π)

内部类只能访问被final修饰的局部变量。

 

常量的书写规范所有字母都大写,由多个单词组成时用“_”连接。PI=3.14

抽象类

当多个类中出现相同的功能,但是功能主体不同,这时可以进行向上抽取。这时,只抽取功能定义,而不取功能主体。

抽象:看不懂。

特点:

1.       抽象方法一定在抽象类中。

2.       抽象方法和抽象类都必须被abstract关键字修饰。

3.       抽象类不可以用new创建对象,因为调用抽象方法没意义。(即不可以实例化)

4.       抽象类中的方法要被使用,必需有子类覆写其所有的抽象方法后,建立子类对象调用。如果子类只覆盖了部分抽象方法,那么子类海是一个抽象类。

eg

abstract class Student
{
abstract void study();
}
class BaseStudent extends Student
{
void study()
{
System.out.println(“base study”);
}
}
class AdvStudent extends Student
{
void study()
{
System.out.println(“adv study”);
}
}
class AbstractDemo
{
public static void main(String[] args)
{
new BaseStudent().study();
}
}


 

11.       接口

格式:

interface{}

接口中的成员修饰符是固定的(少写的话,系统会自动加上):

成员常量:public static final

成员函数:public abstract

接口的出现将“多继承”通过另一种形式体现出来,即“多实现”。

接口是不可以创建对象的,因为有抽象方法。需要被子类实现,子类对接口中的抽象方法全部覆盖后,子类才可以实例化。否则子类是一个抽象类。

interface Inter
{
public static final int x=3;
public abstract void show();
}
class Test implements Inter
{
public void show(){}
}
class InterfaceDemo
{
public static void main(String[] args)
{
Test t=new Test();
System.out.println(t.NUM);
System.out.println(Test.NUM);
System.out.println(Inter.NUM);
}
}


 

注:接口之间可以多继承

eg:

interface A
{
public abstract void showA();
}
interface B
{
public abstract void showB();
}
interface C extends B,A
{
void methodC();
}


 

接口的特点:

接口是对外暴露的规则。

接口是程序的功能扩展。

接口可以用来多实现。

类与接口之间是实现关系,而且类可以继承一个类的同时实现多个接口。

接口与接口之间可以有继承关系。

12.       多态

定义:某一类事物的多种存在形态。

例:动物中猫、狗。

猫这个对象对应的类型是毛类型:

x=new ();

同时猫也是动物中的一种,也可以把猫称为动物。

动物 y=new ();

动物是猫和狗具体事物中抽取出来的父类型。

父类型引用指向了子类对象。

1.       多态的体现。

父类的引用指向了自己的子类对象。

父类的引用也可以接受自己的子类对象。

2.       多态的前提

必须是类与类之间的关系。要么继承、要么实现。

通常还有一个前提:存在覆盖。

3.       多态的好处

多态大大的提高了程序的扩展性。

4.       多态的弊端

提高了扩展性,但是只能使用父类的引用访问福类中的成员。

Animal a = new Cat();         //类型提升,向上转型(此时只具有Animal的各属性方法)。

Cat c = (Cat) a;            //强把a转成猫,并赋值给c

c.catchMouse();                   //c去抓老鼠。

/*以下方式是错的*/

Animal a=new Animal();

Cat c=(Cat)a;  

//只有弗雷德引用指向自己的子类对象时,该应用可以被提升,也可以被强制转换。

总结:多态自始至终都是子类对象在做着变化。

多态中:

静态成员函数的特点:无论编译和运行,都参考左边。

非静态成员函数在多态调用时,编译看左边,运行看右边。

多态中,成员变量的特点:无论编译和运行都参考左边(引用型变量所属的类)