【JavaSE】面向对象程序设计(一)

基本思想

面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)的主要思想是把构成问题的各个事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙一个事物在整个解决问题的步骤中的行为。面向对象程序设计中的概念主要包括:对象、类、数据抽象、继承、动态绑定、数据封装、多态性、消息传递。通过这些概念面向对象的思想得到了具体的体现。

易混概念

“面向对象”和“基于对象”

面向对象的三大特点(封装,继承,多态)缺一不可。通常“基于对象”是使用对象,但是无法利用现有的对象模板产生新的对象类型,继而产生新的对象,也就是说“基于对象”没有继承的特点。而“多态”表示为父类类型的子类对象实例,没有了继承的概念也就无从谈论“多态”。很多流行技术都是基于对象的,它们使用一些封装好的对象,调用对象的方法,设置对象的属性。但是它们无法让程序员派生新对象类型。他们只能使用现有对象的方法和属性。所以当你判断一个新的技术是否是面向对象的时候,通常可以使用后两个特性来加以判断。“面向对象”和“基于对象”都实现了“封装”的概念,但是面向对象实现了“继承和多态”,而“基于对象”没有实现这些,的确很饶口。

“面向过程”和“面向对象”

面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了;面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。
可以拿生活中的实例来理解面向过程与面向对象,例如五子棋,面向过程的设计思路就是首先分析问题的步骤:1、开始游戏,2、黑子先走,3、绘制画面,4、判断输赢,5、轮到白子,6、绘制画面,7、判断输赢,8、返回步骤2,9、输出最后结果。把上面每个步骤用不同的方法来实现。
如果是面向对象的设计思想来解决问题。面向对象的设计则是从另外的思路来解决问题。整个五子棋可以分为1、黑白双方,这两方的行为是一模一样的,2、棋盘系统,负责绘制画面,3、规则系统,负责判定诸如犯规、输赢等。第一类对象(玩家对象)负责接受用户输入,并告知第二类对象(棋盘对象)棋子布局的变化,棋盘对象接收到了棋子的变化就要负责在屏幕上面显示出这种变化,同时利用第三类对象(规则系统)来对棋局进行判定。
可以明显地看出,面向对象是以功能来划分问题,而不是步骤。同样是绘制棋局,这样的行为在面向过程的设计中分散在了多个步骤中,很可能出现不同的绘制版本,因为通常设计人员会考虑到实际情况进行各种各样的简化。而面向对象的设计中,绘图只可能在棋盘对象中出现,从而保证了绘图的统一。

举例:小王老师用电脑上课

老师{
姓名;

上课(电脑){
电脑.开();
电脑.运行();
电脑.关();
}
}

电脑{
开(){}
运行(){}
关机(){}
}

老师 t = new 老师();
t.上课(上课);

这两句发生了什么

image.png
image.png

不要去的对象里赋值

image.png

调用的时候再去赋值

image.png

此时堆栈情况:

image.png

类与对象的关系

类是对象的模板,对象是类的实例
类:对事物的描述,属性+行为
对象:该类事物创建的一个实例,可以通过对象名去调用类里面的属性和行为

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

生命周期:

成员变量是随着对象的加载而加载,随着对象的消失而消失
局部变量是随着方法的运行而出现,随着方法的弹栈而消失

定义位置:

成员变量只能定义在类中(对象里)
局部变量可以定义在方法或者语句中

初始化:

成员变量默认有初始值
局部变量没有默认值,必须赋值后才可以使用

内存分配位置:

成员变量在 堆内存中
局部变量在 栈方法中

匿名对象

public class Test_2 {

	public static void main(String[] args) {
		Phone p = new Phone();
		p.brand = "vivo";
		p.color = "black";
		p.price = 20;
		p.run();
		
		//用匿名对象的方法去调用
		new Phone().brand="oppo";//0x01
		new Phone().color="red";//0x02
		new Phone().price=10;//0x03
		new Phone().run();//0x04
//		以上内容创建了多个对象,在堆内存中存在着4个对象(new了4次)
	}
}

输出结果是默认值?
image.png
原因:
1.每一个 new 都在堆内存中开辟了一个空间,给Phone对象,而Phone对象中有成员变量。且成员变量是有默认值的。接着 .brand .price .color 是把各自Phone对象里的成员变量修改掉。
2.第四个new 也是在堆内存中新建了一个Phone对象,然后要去调用Phone对象的 run() 方法。调用前并没有修改其中成员变量的属性,因此这个run()输出的是三个属性的默认值。
3.执行之后前三个new没有用,就变成了垃圾

image.png

匿名对象使用场景

public class Test_3 {

	public static void main(String[] args) {
		// 普通调用模式
		Phone phone= new Phone();
		phone.run();
		phone.run();
		phone.run();//创建了一个对象,对某个方法调用了多次
		
		//以上调用run()方法的形式,不能简化成匿名对象,它会产生多个对象
		//当调用一个可以简化成匿名对象,多次就不行
		
		
		//简化匿名对象
		
		new Phone().run();//创建了多个对象,并且调用了多次
		new Phone().run();
		new Phone().run();
		new Phone().run();

	}

}
匿名对象好处:

方便书写,但一不小心变海王

减少代码重复性

package javaSE_day6;

public class Test_4 {

	public static void main(String[] args) {

		Phone p=new Phone();
		p.brand="oppo";
		p.color="white";
		p.price=5000;
		p.run();
		
		Phone p1=new Phone();
		p1.brand="oppo";
		p1.color="white";
		p1.price=5000;
		p1.run();
	}
}
/*		从这一段代码中看出这是两个手机,如果10000个怎么办,
		new 10000个吗?*/
	
//解决方法:产生的都是新手机,可以将手机作为一个对象传递到这个方法中
//show()
package javaSE_day6;

public class Test_4 {

	public static void main(String[] args) {

		Phone p=new Phone();
		show(p);
		
		Phone p1=new Phone();
		show(p1); 
	}
/*		从这一段代码中看出这是两个手机,如果10000个怎么办,
		new 10000个吗?*/
	
//解决方法:产生的都是新手机,可以将手机作为一个对象传递到这个方法中
	public static void show(Phone p){
		p.brand="oppo";
		p.color="white";
		p.price=5000;
		p.run();
	}
}

## 封装 对属性封装,增加安全性
setXxx() , getXxx()
下面的例子中将Person的age属性 设置为私有,用set get进行封装 ```java public class Person { //属性 private int age; //设置值 public void setAge(int a){ if (a>0 && a<200) { age=a; //赋值 }else { System.out.println("你的年龄非法"); } } //取值 public int getAge(){ return age; } //行为 public void speak(){ System.out.println("age="+age); } } ``` ```java public class Test_01 {
public static void main(String[] args) {
	// 创建对象
	Person p = new Person();
	//对属性赋值
	p.setAge(40);
	System.out.println(p.getAge());
	//调用方法
	p.speak();
}

}

## 构造方法
对类进行初始化(对类里面的属性进行赋值),每当new一个对象时就会调用<br />编译器:你有就用你自己的,没有我给你加一个
```java
public class Student {
	//属性
	private String sex;
	private int age;
	
	//构造方法
	Student(){
		System.out.println("A");
	}

	//行为
	public void show(){
		System.out.println("sex:"+sex+"\tage="+age);
	}
}

public class StudentTest {

	public static void main(String[] args) {
		// 创建对象
		Student stu = new Student();//new的时候过去调用构造方法
		stu.show();
	}
}

image.png

构造方法—“先天” 赋值

public class Student {
	//属性
	private String sex;
	private int age;
	
	//构造方法
	Student(String s,int a){
		System.out.println(s+a);
		sex=s;
		age=a;
	}

	//行为
	public void show(){
		System.out.println("sex:"+sex+"\tage="+age);
	}
}

public class StudentTest {

	public static void main(String[] args) {
		// 创建对象
		Student stu = new Student("男",78);
		stu.show();
	}

}

image.png

构造方法内存图

image.png

构造方法_总结

1.没有返回值,因为对象创建之后就结束了,不需要结果,void不需要写,要区别一般方法。
2.一个类可以有多个构造方法,它们都是以重载的格式出现的
3.构造方法也有return语句的,用于初始化结束(加不加都行,后面都没有语句了)
4.构造方法 可以用private修饰,但是没有意义

构造方法和一般方法的区别

1.写法不同
2.运行上有差别
构造方法:对象一创建就会调用相应的构造方法
一般方法:对象创建之后调用的
3.调用的次数不同
构造方法在创建对象的时候只调用一次
一般方法可以用对象名调用多次

构造之后可以封装吗?

可以

souce里面找set get 直接封装到类里

public class Student {
	//属性
	private String sex;
	private int age;
	
	public String getSex() {
		return sex;
	}

	public void setSex(String sex) {
		this.sex = sex;
	}

	public int getAge() {
		return age;
	}

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

	//构造方法
	Student(String s,int a){
		System.out.println(s+a);
		sex=s;
		age=a;
	}

	//行为
	public void show(){
		System.out.println("sex:"+sex+"\tage="+age);
	}
}

在实例中用stu去调用set

public class StudentTest {

	public static void main(String[] args) {
		// 创建对象
		Student stu = new Student("男",78);
		
		stu.setSex("女");
		stu.setAge(18);
		
		stu.show();
	}

}

image.png

this的指向作用——渣出天际的this

this:我是this,每天都会有形形色色的对象找到我,并和我在堆和栈中进行一些深入交流。这个例子中我在print()方法的房间里,你们哪个对象谁一调用print(),进来就会找到我,我就会马上记住你的地址,顺着你的地址,我就可以找到你在堆中成员变量的值。其实当你进来调用的那一刻,我就是你的人了。但我不能只是你一个人的,我愿意拥抱任何一个调用我的对象。

image.png
image.png
image.png

构造方法“真正的”书写格式

有 this

    private String name;	
	private String sex;
	public Teacher(String name, String sex) {
		this.name = name;
		this.sex = sex;
	}

总结:

构造方法是对象初始化调用的?给哪个对象初始化了?
通过this关键字记录住对象的地址,并通过this来明确初始化对象

在构造方法中调用其他构造方法
this(参数);

this到底代表了什么?
this代表的就是一个对象,代表哪一个对象了?代表的是当前对象,哪个调用所在方法的this就指向哪个对象

构造方法可以调用一般,一般方法不可以调用构造方法

一个构造方法只可以构造一个其他的构造方法(this写在第一行)

不能递归调用

static

静态调用,静态是随着类的加载而加载
1 . 用来修饰成员 2 . 静态不能访问非静态

//静态调用,类名.方法名();
	Person.sleep();

//普通方法调用
	Person p=new Person();
	p.sleep();

态不能访问非静态_例01:

public class Person {
	private String sex;
	private int age;
	private String name;
	
	public static void sleep(){
		System.out.println("sex"+sex);
		System.out.println("age"+age);
		System.out.println("name"+name);
	}
}

报错原因:静态不能访问非静态
分析过程:
1.内存里加载一个对象Person
2.对象里,静态的属性/方法先被加载,这里会先加载sleep()
3.而sleep()里的age,name,sex属性,还没有加载进来,在内存里找不到,报错。
如何解决:给属性也加上静态。或者把方法里的static去掉。

正解:

public class Person {
	private static String sex;
	private static int age;
	private static String name;
	
	public static void sleep(){
		System.out.println("sex"+sex);
		System.out.println("age"+age);
		System.out.println("name"+name);
	}
}

静态不能访问非静态_例02
image.png
在静态方法sleep()里定义了一个“非静态”的局部变量sex,
sleep()是静态方法,它里面代码块,syso方法里的sex,age,name当然也是静态的。本来syso里的sex是静态的,现在被新定义的局部变量一搞,变成非静态的了。

那么把新定义的局部变量加一个static试试
image.png
发现还是报错了。
原因是,静态只能修饰成员变量,不可以修饰局部变量。

那么什么时候定义静态变量呢?

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

  1. 所属范围不同:静态是属于类的,成员变量是属于对象的
  2. 调用不同:静态变量可以被对象和类调用(一般都用类名调用)

成员变量只能由对象调用

  1. 加载时期不同:静态变量随着类的加载而加载,成员变量随着对象的加载而加载
  2. 内存存储的区域不同:静态变量是存储在方法区的,成员变量是存储在堆内存中的

单例

保证对象唯一性

(test里去new对象会报错,因为new的时候要去调用构造,而单例用的是私有化构造方法)

分类:饿汉;懒汉。

懒汉可以延迟加载,饿汉不行,饿汉一加载就会创建一个对象,懒汉是这个对象一开始是null,当进入方法时创建对象

饿汉式单例如下:

public class Single {
	//私有化构造方法
	private Single(){
		
	}
	
	//实现对象的可控性,在本类中创建一个对象
	static Single s=new Single();
	//对外提供一个访问的方法
	public static Single getInstance(){
		return s;
	}
}
public class SingleTest {

	public static void main(String[] args) {
		//静态调用要用  类名.方法名
		Single.getInstance();
		//new对象的方式,此调用不符合单例格式,不要用这个格式
		//new Single.getInstance();
		
		//s1,s2都是同一个对象
		Single s1 = Single.getInstance();
		System.out.println(s1);
		Single s2 = Single.getInstance();
		System.out.println(s2);
	}

}

image.png

饿汉式单例的弊端:

不管后期有没有用到这个对象,都进行了对象的创建
这样做的好处是编写简单,但无法做到延迟加载

延迟加载例:

用户名密码登录,首先要拿一个验证,要去查询有没有这个东西,如果这个时候不去查询,不去做一个延迟加载,用户名和密码一写就登陆成功就不对了

懒汉式单例如下

//懒汉式
public class Single02 {
	//1.私有化构造方法
	private Single02(){
		
	}
	//2.在本类中创建对象
	static Single02 s = null;
	//3.对外提供一个可访问方法
	static Single02 getInstance(){
		if (s==null) {
			s=new Single02();
		}
		return s;
	}
}
public class Single02Test {

	public static void main(String[] args) {
		//静态调用
		Single02.getInstance();
	}

}

再看一个例子(如下):

public class SuperMan {
	//属性
	private String name;
	//1.定义构造
	private SuperMan(String name) {
		this.name = name;
	}
	//2.在本类中创建一个对象
	static SuperMan s = new SuperMan("小强");
	//在本类中定义一个方法
	public static SuperMan getInstance(){
		return s;
	}
	
	public void fly(){
		System.out.println(name+"可以飞");
	}
}
public class SuperManTest {

	public static void main(String[] args) {
		// 用单例的方法解决了对象唯一性
		SuperMan s33=SuperMan.getInstance();
		System.out.println(s33);
		s33.fly();
        
		SuperMan s2=SuperMan.getInstance();
		System.out.println(s2);
		s2.fly();
	}

}

image.png

类文件的创建过程:

声明 类/对象名为 SuperMan
声明SuperMan的一个私有属性name(成员变量)
定义SuperMan类的私有构造(用于创建对象时先天赋值),指明以后要创建SuperMan类的对象,就要传进来一个name,把这个name赋给当前类的名字
创建一个SuperMan类的一个对象 ,并带上名字“小强”,给这个新建的对象起名为s;
在SuperMan类中提供一个可供外界访问的方法,到时候外界通过 "变量名点方法名()"的形式调用这里;这个方法的写法是 “类名空格方法名”,由于要在外界访问,所以要public,又因为外界main()是用的static静态调用,所以这里也要static;方法返回的是第二步新建的对象s,这个对象s目前已被创建,因此在内存中占一定的空间,且它只有一个属性,也就是它的名字“小强”;因此这里完整的写法是“public static 大写类名 空格 方法名(){ return 新建对象变量名,这里是s;}”
就是说此后外界只要调用getInstance(),就会得到关于对象s的所有信息;当然必须要有一个和对象s完全匹配的载体,来全盘接收对象s的信息。所以只需要定义一个和对象s相同的类,再取一个名字,就可以把getInstance()的值赋过来了;
最后定义了一个fly()方法,可以看出要使用fly()方法,必须要有name的值,这里的name指向的其实是this.name,也就是当前类的成员变量name的值,当前类的类名已经有了,是新建对象s传进去的“小强”。
所以外界成功调用fly()方法的条件只有一个,那就是用同样类的变量去调用。刚好getInstance()那里定义了一个同类的变量,拿来用就好。

回调过程中的思路:

SuperMan.getInstance()返回的东西赋给s33,事实上返回的东西是对象s,而对象s只有一个属性,就是它的名字“小强”。
这里输出的是s33的地址,也就是s的地址
对象s33调用fly()方法,fly()方法中要用到当前类的成员变量name的值(this.name),通过构造赋值,得知this.name是“小强”

继承

继承的好处:

提高了代码的复用性,给另一个特征提供了前提(多态)

什么时候继承?

必须保证类与类之间存在关系,肉食动物是动物的一种,苹果是水果的一种,存在的是is—a的关系

java中继承的特点:

java允许单继承,不能直接继承多个类,将多继承以另一种形式体现
**单继承:**子类只可以有一个父类
**多继承:**一个子类可以有多个父类

单继承体系_;例:

//子类Student
public class Student extends Person {
	
	Student(String name, int age) {
		super(name, age);
	}

	//构造方法
	
	//行为
	void study(){
		System.out.println("name"+name+"\tage"+age);
	}

}
//子类Teacher
public class Teacher extends Person {
	//构造方法
	Teacher(String name,int age) {
		super(name,age);
	}
	//行为
	void work(){
		System.out.println("name"+name+"\tage"+age);
	}
}

//父类Person
public class Person {
	//属性
	String name;
	int age;
	//构造方法
    Person(String name, int age) {
		this.name = name;
		this.age = age;
	}
}
//测试Test
public class Test {

	public static void main(String[] args) {
		//创建对象 
		Student stu = new Student("小明", 30);
		stu.study();
		
		Teacher tea = new Teacher("coco", 30);
		tea.work();
		//当new对象时,找到构造方法后发现,构造方法里存在super,代表找父类
		//相当于把参数传到了父类,这里父类做了一个赋值操纵
		//把当前对象的属性进行了赋值,所以打印时是有结果的
	}
}

this和super的区别:

this代表的是当前对象
super代表的是父类的引用,super()不可以单独使用,必须要super.变量;或者super();

public class Fu {
	int num=4;
}
public class Zis extends Fu {
	int num=5;
	
	void show(){
		System.out.println("num="+super.num);
	}
}
public class ZisTest {

	public static void main(String[] args) {

		
		Zis f = new Zis();
		f.show();
	}
}
覆盖

子类在运行时,它里面定义了一个和父类一样的方法名,子类里面的方法重写(覆盖)父类里的方法;

什么情况下去用覆盖呢?

子类要扩展父类功能的时候

继承的弊端

继承相当于打破了封装

如果不让父类不让子类重写怎么办?

在方法上加一个final就可以了

继承在程序中的体现
1.成员变量
2.成员方法
3.构造方法

隐式super()

public class Test {

	public static void main(String[] args) {
		// 创建对象
		Zi zi=new Zi();//Fu Zi
	}

}
public class Zi extends Fu{
	Zi() {
		super();//这个子类的构造里存在一个隐式的super() 
		System.out.println("Zi");
	}
}
public class Fu {
	public Fu() {
		System.out.println("Fu");
	}
}

image.png
子类会继承父类的内容,所以子类在初始化时,必须要先找到父类去执行父类的初始化操作,才可以更方便地调用父类中的内容。

出现super报错的情况;为什么?

因为参数列表不对应,在父类中没有找到相应的构造方法

如何解决?

在子类的super里传参,或者修改/增加父类的构造

经典父A子C

public class Fu1 {
	Fu1() {
		System.out.println("fu...A");
	}
	Fu1(int x){
		System.out.println("fu...B");
	}
}
public class Zis extends Fu1{
	Zis() {
		System.out.println("Zis....C");
	}
	Zis(int a){
		System.out.println("Zis...D");
	}
}
public class ZisTest {

	public static void main(String[] args) {
		//创建子类对象
		Zis z =new Zis();
	}
}

image.png

经典父A子D

提示:继承体系中,默认添加的隐式super()是不带参数的

public class ZisTest {

	public static void main(String[] args) {
		//创建子类对象
		Zis z =new Zis(5);
	}
}
public class Zis extends Fu1{
	Zis() {
		System.out.println("Zis....C");
	}
	Zis(int a){
		System.out.println("Zis...D");
	}
}
public class Fu1 {
	Fu1() {
		System.out.println("fu...A");
	}
	Fu1(int x){
		System.out.println("fu...B");
	}
}

image.png

如何输出父B子D呢?

就需要手动在子类里添加带参super
这里就不演示了

this()和super()不能同时出现在一个构造里。

final

final是一个关键字:可以修饰类,方法,变量(成员,局部,静态)

特点:
1.final修饰的类是一个最终类,不能再派生子类
2.如果类中有些方法不需要重写,让指定的方法加一个final即可
因为final它是一个最终方法,不可以重写
3.final修饰量是一个常量,只能被赋值一次
什么时候去定义一个常量?当程序中不需要更改的时候就可以定义为一个常量
4.final定义的规范:被final修饰的常量字母都是大写的,如果有多个字母组成,就用_来连接 final doule PI=3.14;
5.final会把变量最终话,只能显示赋值

public class FinalTest {
	int a;
	public static void main(String[] args) {
		FinalTest f =new FinalTest();
		System.out.println(f.a);
	}
}

image.png这里的 a是有默认值的
而如果在int a 前加一个final,会报错
image.png
报错原因:说是没定义,其实是因为final修饰的变量必须要先赋值,在使用

image.png

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DFminer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值