黑马程序员_Java基础_面向对象,封装,继承,单例设计模式,构造函数,构造代码块

时间:2023-02-11 17:14:40
一,面向对象
1,基本理解

面向对象其实是一种思想,现实生活中我们所看到的东西,包括想到的一个概念都可以看做是一个对象。

如果有人问到你对面向对象的理解,可以举一个例子说明,比如:我们去餐馆就餐,首先我们需要叫来服务员点菜,因为服务员具备点菜的功能,点完餐后,服务员会将菜单给厨师,厨师具有做饭的功能,会根据你的选择做出你想吃的东西,然后服务员给你上菜,你就可以开始吃了。这其中的面向对象包括你调用服务员的点菜方法,服务员又调用厨师做菜的方法,你又调用服务员传菜的方法,这一切都是面向对象,而你不用去管服务员是如何做的,更不用去管厨师是怎么做的,你只需要调用服务员的相应功能得到饭即可。

2,其实开发过程中实际就是找对象,调用对象的某些功能实现我们的需求。

如果没有对象,我们只需要造出一个对象,然后在对象中写出实现我们需求的功能局可以了。

3,java中对对象的描述:是通过类的形式描述的。对事物的描述通常只需要关注事物的属性和行为两个方面。

4,java中类与对象的关系:

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

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

5,对事物描述的举例:

需求:对小汽车的描述。

分析:属性--轮胎数。行为--运行。

定义类其实就是定义类中的成员,成员包括成员变量(属性)和成员方法(行为)。

成员变量特点:(1)定义在类中,整个类都可以访问。(2)存在于对内存的对象中,并且随着对象的

创建而存在,随着对象的消失而消失。(3)成员变量都有默认的初始值。

局部变量:(1)定义在函数,语句,局部代码块中,只有所属的区域有效。(2)存在于栈内存中,随

着所属区域的执行而存在,结束而释放。(3)局部变量没有默认初始化值。

class Car
{
//描述颜色
String color = "红色";
//描述轮胎数
int num = 4;

//运行行为。
void run()
{

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

}
class CarDemo
{
public static void main(String[] args)
{
//创建car对象,调用run方法
Car car = new Car();
c.run();
c.color = "白色";
c.run();
}
}
6,匿名对象的使用
匿名对象使用方式一:当对对象的方法只调用一次时,可以用匿名对象来完成,这样写比较简化。
如果对一个对象进行多个成员调用,必须给这个对象起个名字。
匿名对象使用方式二:可以将匿名对象作为实际参数进行传递。

class Car
{
//描述颜色
String color = "红色";
//描述轮胎数
int num = 4;

//运行行为。
void run()
{

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

}
class CarDemo
{
public static void main(String[] args)
{
//创建car对象,调用run方法
Car car = new Car();
c.run();
c.color = "白色";
c.run();

//使用匿名内部类调用run方法
new Car().run();
}
}
二,面向对象的特性之封装
1,封装:是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。
2,封装的好处:将变化隔离,便于使用,提高重用性,提高安全性。
3,封装的原则:将不需要对外提供的内容都隐藏起来。
把属性都隐藏,提供公共方法对其访问。

4,private访问修饰符只是用于修饰类中的成员(成员变量,成员函数),并且只在本类中有效。

使用private将成员变量设置为私有访问权限,通过get和set方法提供获取和设置成员变量的方法,set方法的返回类型是void,get方法返回类型是成员变量的类型。

私有仅仅是封装的一种表现形式,不可将私有理解成是封装。

class Person
{
private int age;
public void setAge(int a)
{
if(a>0 && a<130)
{
age = a;
speak();
}
else
System.out.println("年龄错误");
}

public int getAge()
{
return age;
}
private void speak()
{
System.out.println("age="+age);
}
}

class Test1
{
public static void main(String[] args)
{
Person p = new Person();

//p.age = -20;
p.setAge(-40);
//p.speak();
}
}

三,构造函数以及构造代码块的使用

1,构造函数:创建对象时调用的函数。
作用:对对象进行初始化。
一个类中如果没有定义构造函数,那么该类中会有一个默认的无参数的构造函数。
如果类中定义了指定的构造函数,则默认的构造函数则不存在了。
2,构造函数与一般的函数的区别:
(1)构造函数在对象创建时就会调用与之对应的构造函数,对对象进行初始化。
而一般函数在对象建立后,需要函数功能时才调用。
(2)构造函数在对象创建时,只调用一次,并且只会调用一次,而一般函数在对象创建后可以被
多次调用。
3,什么时候定义构造函数?
在描述事物时,该事物已存在就具备了一些内容,这些内容都定义在构造函数中,构造函数可以有
多个,对于不同的对象进行针对性的初始化。多个构造函数在类中是以重载的形式体现的。
4,需要注意的是:一般函数是不能调用构造函数的,如果构造函数前面加了返回值类型,就变成了
一般的函数。构造函数中是有return语句的。
5,构造代码块的使用;
作用:给对象进行初始化。
对象一建立就运行,而且优先于构造函数执行。
和构造函数的区别:
构造代码块是给所有对象进行统一初始化,
而构造函数是给对应的对象初始化。

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

class Person
{
private String name;
private int age;
//构造代码块:该代码块会在每次创建对象的时候先执行一次,然后构造函数才执行
{
cry();
}

Person()
{
System.out.println("A: name="+name+",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);
}

public void cry()
{

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

class Test
{
public static void main(String[] args)
{
Person p1 = new Person();
Person p2 = new Person("lisi");
}
}

四,面向对象之单例设计模式

1,java中一共有23中设计模式。所谓的设计模式就是解决一类问题的行之有效的方法。

2,单例设计模式是23中设计模式中的一种,它是用来解决内存中只存在某一个对象。

3,保证内存中对象的唯一的方式:

(1)为了避免其他的程序过多的建立该类的对象,先禁止其它程序建立该类对象。方法是将构造函数私有化。

(2)为了让其他程序可以访问到到该类对象,只好在自己的类中建立一个该类对象。方法是自定义一个类。

(3)为了让其他程序对自定义的对象的访问,可以对外提供访问该类的方式。提供一个方法可以获取该对象。

示例代码:

public class SingleDemo {

public static void main(String[] args) {

Single s = Single.getInstance();
s.setNum(10000);
System.out.println(s.getNum());
}

}

class Single {
private Single() {
}

static Single single = new Single();

public static Single getInstance() {
return single;
}

private int num;
public void setNum(int num) {
this.num = num;
}
public int getNum() {
return num;
}
}
使用单例设计模式的时候要注意,程序该怎么编写就怎么编写,只需要加上保证内存唯一的那一部分代码即可,所以这三步可以作为一个模板。

4,单例设计模式——饿汉式,饿汉式是指,在类一加载就已经创建好了对象。

class Single {
private Single() {
}

private static Single single = new Single();

public static Single getInstance() {
return single;
}
...
}
5,单例设计模式——懒汉式,懒汉式是指在类加载到内存时,对象还没有被创建,而是调用获取对象的方法时才创建对象。也叫做对象的延迟加载。

class Single {
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;
}
...
}
单例设计模式原则:在使用单例设计模式的时候建议使用饿汉式。

五,面向对象之继承

1,java中的继承只有单继承,可以通过人这个例子来理解,因为每个人只能有一个父亲。但是java是支持多重继承的,也就是一个类可以被多个类继承,这样所有的字类就拥有了父类的方法。

之所以不支持多继承原因是:当继承多个父类时,如果多个父类中有多个相同的方法,那么子类就不知道到底使用父类的哪个方法,但是一个父亲可以拥有多个孩子。但是java中支持多实现。也就是后面将要讲到的接口。

2,继承的好处是:提高了代码的复用性,并且让类与类之间产生了关系,也就有了后面的多态。

3,在使用继承的时候一定要注意,不要为了获取其他类的功能,简化代码而使用继承。类与类之间必须要有所属关系时才使用继承。

4,继承中的成员变量的特点

继承中如果子类出现非私有的和父类相同的成员变量子类要访问本类的成员变量时,使用this关键字,要访问父类的成员变量的时候,用super关键字。super关键字和this关键字的用法几乎一样,this指的是本类对象的引用,super指的是父类对象的引用。

5,继承中函数的特点

当子类中出现和父类一模一样的函数时,当子类对象调用该函数的时候,会运行子类的函数的内容。这种情况叫做重写。当子类继承了父类的,沿袭了父类的功能,到了子类中子类具备该功能,但是功能的内容却和父类不一样,这时没有必要重新定义该功能,可以使用重写父类的该功能。子类覆盖父类的方法时要注意:子类方法的权限要大于父类的方法。静态只能覆盖静态。

6,继承中构造函数的特点

在对子类进行初始化的时候,父类的构造方法也会运行,因为子类的构造方法的第一句默认调用父类的空参数的构造方法。子类的所有构造函数的第一句默认的都是super()。

为什么子类中一定要访问父类的构造方法呢?是因为子类可以直接获取父类中的数据,所以子类在建立对象的时候,要先查看父类是如何对这些数据进行初始化的。所有的子类的在创建对象的时候要先访问一下父类的构造函数,如果没有可以手动通过super关键字定义。如果父类中没有空参数的构造函数,子类构造函数中一定要手动super关键字访问父类带参数的构造函数,而且super关键字要放在子类构造函数的第一行。

示例:定义一个Person类,有设置姓名和年龄,父类中定义sayHello方法,子类中重写该方法,通过子类对象调用。

class Person {
public String name;
public int age;
int num = 12;
Person(String name,int age) {
this.name = name;
this.age = age;
}


/*public void setName(String name) {
this.name = name;
}


public void setAge(int age) {
this.age = age;
}*/


public void sayHello() {
System.out.println(name + ":" + age);
}
}


class Student extends Person {
int num = 13;
Student(String name,int age) {
super(name,age);
}
// 重写父类中的sayHello方法
public void sayHello() {
System.out.println("我叫:" + name + ",今年" + age + "岁了");
System.out.println(super.num + "::" + this.num);//默认的this,可以省略
}
}


class Demo {
public static void main(String[] args) {
Student s = new Student("zhangsan",25);
/*s.setName("zhangsan");
s.setAge(25);*/
s.sayHello();
}
}