Java-面向对象

软件生命周期:
软件生命周期: 软件的产生直到报废的整个过程.
软件生命周期内有:问题定义, 可行性分析, 总体描述, 系统设计,编码, 调试和测试, 验收与运行, 维护升级到废弃等阶段

1):问题的定义及规划: 此阶段是软件开发方与需求方共同讨论,主要确定软件的开发目标及其可行性。
2):需求分析: 在确定软件开发可行的情况下,对软件需要实现的各功能进行详细分析。需求分析阶段是一个很重要的阶段,这一阶段做得好,将为整个软件开发项目的成功打下良好的基础。
3):软件设计: 此阶段主要根据需求分析的结果,把整个软件系统划分为大大小小的多个模块,设计出每一个模块的具体结构。如系统框架设计,数据库设计等。软件设计一般分为总体设计和详细设计。
4):程序编码: 此阶段是将软件设计的结果转换成计算机可运行的程序代码。在程序编码中必须要制定统一,符合标准的编写规范。以保证程序的可读性,易维护性,提高程序的运行效率。
5):软件测试: 在软件设计完成后要经过严密的测试,以发现软件在整个设计过程中存在的问题并加以纠正。整个测试过程分单元测试(白盒)、集成测试(黑盒,功能测试、强度性能测试)以及系统测试三个阶段进行。测试的方法主要有白盒测试和黑盒测试两种。在测试过程中需要建立详细的测试计划并严格按照测试计划进行测试,以减少测试的随意性。
6):运行维护: 安装部署软件系统,修复软件中存在的bug和升级系统。在软件开发完成并投入使后,由于多方面的原因,软件不能继续适应用户的要求。要延续软件的使用寿命,就必须对软件进行维护。软件的维护包括纠错性维护和改进性维护两个方面。

软件设计原则:
为了提高软件的开发效率,降低软件开发成本,一个优良的软件系统应该具有以下特点:
1,可重用性:遵循DRY原则,减少软件中的重复代码。
2,可拓展性:当软件需要升级增加新的功能,能够在现有的系统架构上方便地创建新的模块,而不需要改变软件现有的结构,也不会影响以及存在的模块。
3,可维护性:当用户需求发生变化时,只需要修改局部的模块中的少量代码即可。

如何让软件系统达到上述的特点,我们对模块的要求:
1):结构稳定性:在软件设计阶段,把一个模块划分为更小的模块时,设计合理,使得系统结构健壮,以便适应用户的需求变化。
2):可拓展性:当软件必须增加新的功能时,可在现有模块的基础上创建出新的模块,该模块继承了原有模块的一些特性,并且还具有一些新的特性,从而实现软件的可重用和可拓展性。
3):可组合性:若干模块经过组合,形成大系统,模块的可组合性提高软件的可重用和可维护性,并且能简化软件开发过程。
4):高内聚性:内聚,强调一个模块内的功能联系,每个模块只完成特定的功能,不同模块之间不会有功能的重叠,高内聚性可以提高软件的可重用性和可维护性。
5):低耦合性:耦合,强调的是多个模块之间的关系,模块之间相互独立,修改某一个模块,不会影响到其他的模块。低耦合性提高了软件的可维护性。
软件的开发方式:
1):面向过程的开发
2):面向对象的开发
面向过程:一种较早的编程思想,顾名思义该思想是站在过程的角度思考问题,强调的就是功能行为,功能的执行过程,即先干啥,后干啥。而每一个功能我们都使用函数(类似于方法)把这些步骤一步一步实现,使用的时候依次调用函数就可以了。

面向过程的设计:
最小的程序单元是函数,每个函数负责完成某一个功能,用以接受输入数据,函数对输入数据进行处理,然后输出结果数据.
整个软件系统由一个个的函数组成,其中作为程序入口的函数称之为主函数,主函数依次调用其他函数,普通函数之间可以相互调用,从而实现整个系统功能.
面向过程的缺陷:
面向过程的设计,是采用置顶而下的设计方式,在设计阶段就需要考虑每一个模块应该分解成哪些子模块,每一个子模块有细分为更小的子模块,如此类推,直到将模块细化为一个个函数.
存在的问题:
1):设计不够直观,与人类的习惯思维不一致.
2):系统软件适应性差,可拓展性差,维护性低.

面向过程最大的问题在于随着系统的膨胀,面向过程将无法应付,最终导致系统的崩溃。为了解决这一种软件危机,我们提出面向对象思想。
面向对象:一种基于面向过程的新的编程思想,顾名思义该思想是站在对象的角度思考问题,我们把多个功能合理的放到不同对象里,强调的是具备某些功能的对象。
具备某种功能的实体,称为对象.
面向对象最小的程序单元是:类.
面向对象更加符合我们常规的思维方式,稳定性好,可重用性强,易于开发大型软件产品,有良好的可维护性。
在软件工程上,面向对象可以使工程更加模块化,实现更低的耦合和更高的内聚
当然上述例子仅仅只是说明了面向对象的一个特征——封装。除此之外面向对象还有两大特征,我们在具体讲解到的时候再做分析。
三大特征:
1:封装(Encapsulation);
2:继承(Inheritance);
3:多态(Polymorphism);
封装是指将对象的实现细节隐藏起来,然后通过公共的方法来向外暴露该对象的功能。
继承是面向对象实现软件复用的重要手段,当子类继承父类后,子类是一种特殊的父类,能直接或间接获得父类里的成员。
多态是可以直接把子类对象赋给父类变量,但是运行时依然表现出子类的行为特征,这意味着同一类型的对象在运行时可能表现出不同的行为特征。
注意:
千万不要误解为面向对象一定就优于面向过程的设计
面向对象有三大特征:
1):封装
2):继承
3):多态
4):抽象

什么是抽象:从特定的角度出发,从已经存在的一些事物中抽取我们所关注的特性、行为,从而形成一个新的事物的思维过程,是一种从复杂到简洁的思维方式。

此时,通过对多个同类型的对象分析,我们可以把对象抽象成类。

类:
具有相同特性(状态)和行为(功能)的对象的抽象就是类。因此,对象的抽象是类,类的具体化就是对象,也可以说类的实例是对象,类实际上就是一种数据类型。
类具有特性,对象的状态,用成员变量来描述,封装数据。
类具有功能,对象的行为,用方法来描述。

什么是类:类是对象的类型/模板。创建一个对象,就是使用一个类作为构建该对象的基础。
实际上,解释类和对象的关系确实存在着“先有蛋还是先有鸡”的问题,描述类少不了对象,相反,描述对象也少不了类。
不过在面向对象编程里,我们肯定是先有类的。如果没有类,对象就没法实例化。

对象是类的实例,类是对象的模板。
类的定义:

[修饰符] class 类名
{
0~N个成员变量(字段/Field),不要叫属性(JavaBean中特殊的一种成员).
0~N个方法
}

定义类的注意:
1):如果类使用了public修饰符,必须保证当前文件名称和当前类名相同.
2):类名使用名称表示,类表示某一类事物,首字母大写,如果是多个单词组成使用驼峰表示法.
如: EmployeeAdvanceSetting.
3):在面向对象的过程中,各位定义类的时候,专门为描述对象提供一个类,该类不需要main方法.
我们专门再定义一个测试类/演示类来运行程序.

1):根据类来创建对象的语法:
类名 变量 = new 类名();
2):给字段设置值:
对象变量.字段名 = 该类型的值;
3):获取字段的值:
该字段类型 变量 = 对象变量.字段值;
4):通过对象调用方法.
对象变量.方法(实参);

对象的打印和比较对象以及匿名对象和对象的生命周期:

1):对象的打印操作:
打印对象的时候,打印出来的效果,如:Servant@3294e4f4.
类的名称@十六进制的hashCode.
其实,我们打印对象,往往关系的是该对象中的数据是多少,期待讲解Obejct的toString方法.
2):对象比较操作:
==号:对于基本数据类型来说,比较的是值.
对于引用数据类型来说,比较的是在内存中的地址值.
每次使用new关键字,都表示在堆值新开辟一块内存空间,不同的内存空间,地址值不同.
需求:如果对象拥有完全相同的数据,我就想让他们相等. 期待:Object的equals方法.
3):对象的生命周期:
对象什么时候出生:每次使用new关键字的时候,就会在内存开辟新的空间,此时对象开始存在.
对象的结束:当堆中的对象,没有被任何变量所引用,此时该对象就成了垃圾,就等着垃圾回收器(GC)来回收该垃圾,当被回收的时候,对象被销毁了,回收垃圾的目的,释放更多的内存空间.
4):匿名对象: 没有名称的对象,创建对象之后没有赋给某一个变量.
new Servant();//匿名对象,匿名对象只是在堆中开辟一块新的内存空间,但是没有把该空间地址赋给任何变量.
因为没有名称,匿名对象仅仅只能使用一次,一般的,把匿名对象作为方法的实参传递.
创建一个学生对象的时候,代码如下:
Student s1 = new Student();
这个代码特别像:在调用一个方法名称为Student的无参数方法.

我们把这种特殊的方法,称之为构造方法,构造器(构造函数/构造子:Constructor).
但是我们发现在Student类中,却找不到该构造方法,找不到,却又没有报错.
编译器在编译源文件的时候,会创建一个缺省的构造器.

构造器的作用:
1):创建对象,凡是必须和 new 一起使用.
2):完成对象的初始化操作.
构造器的特点:
1):构造器的名称和当前所在类的名称相同.
如果构造器名称叫XXX, new XXX();你看出来创建的是什么类型的对象吗?
2):禁止定义返回类型,千万不要使用void作为返回类型. void Student(){}这是普通方法.
假设需要写返回类型,也应该这样写:Student Student(); 既然所有的返回类型都和所在类名相同,何必再写.
3):在构造器中,不需要使用return语句.
其实构造器是有返回值的,返回的是当前创建对象的引用.

编译器创建的默认构造器的特点:
1):符合构造器特点.
2):无参数的.
3):无方法体.
4):如果类A没有使用public修饰,则编译器创建的构造器也没有public修饰.
使用了public修饰,则编译器创建的构造器也使用public修饰.
构造器:如果我们没有显示提供构造器,则编译器在编译时创建一个缺省的构造器.
但是,如果我们显示定义了一个构造器,则编译器不再创建默认构造器.
------------------------------->推论:某一个类,至少存在一个构造器.
对象都有name和age两个状态,但是不同对象的name的值和age的值是不一样的.
也就是说,不同对象的name和age是不一样的.
Person p1 = new Person();
p1.name=”春哥”;
p1.age = 31;
Person p2 = new Person();
p2.name = “凤姐”;
p2.age = 30;

System.out.println(p1.name +”, ” + p2.name);//春哥,凤姐

也就说name和age是属于对象的,不同对象的name和age是不一样的.但是在生活中有些东西不是属于某一个具体对象的,而是属于整个事物的类型,比如:全世界人的总数,人的毁灭的行为.
毁灭的行为,应该属于人类,而不属于某一个人.

状态和行为应该有对象和类型之分.
有的状态和行为应该属于对象,不同的对象,状态和行为可以不一样.
有的状态和行为应该属于类型,不属于对象.
问题:现在如何表示人类毁灭这个行为(destory).
要解决该问题,得学习static修饰符.
修饰符:
晚上11点,班长在写代码,遇到一个bug,班长解决问题后,睡觉去了。
晚上11点,班长漫不经心地在写代码,遇到一个很简单的bug,班长稀里糊涂解决问题后,满不在乎地睡觉去了。
晚上11点,帅气的班长认认真真地在写代码,遇到一个超难的bug,班长呕心沥血解决问题后,眉开眼笑地睡觉去了。

static修饰符表示静态的,可修饰字段、方法、内部类,其修饰的成员属于类,也就是说static修饰的资源属于类级别,而不是对象级别。

static的真正作用:用来区别字段,方法,内部类,初始化代码块是属于对象还是属于类本身。
static修饰符的特点:
1):static修饰的成员(字段/方法),随着所在类的加载而加载.
当JVM把字节码加载进JVM的时候,static修饰的成员已经在内存中存在了.
2):优先于对象的存在.
对象是我们手动通过new关键字创建出来的.
3:satic修饰的成员被该类型的所有对象所共享.
根据该类创建出来的任何对象,都可以访问static成员.(狗天生就吃屎.)
剧透:表面上通过对象去访问static成员,其本质依然使用类名访问,和对象没有任何关系(通过反编译看到的).
4):直接使用类名访问static成员
因为static修饰的成员直接属于类,不属于对象,所以可以直接使用类名访问static成员.
static成员在JVM中的存储:储存于方法区里,不在栈和堆里
类成员和实例成员的访问 :
类中的成员:字段,方法,内部类.
类成员: 使用static修饰的成员.
实例成员: 没有使用static修饰的成员.

类成员只能访问类成员,实例成员只能访问实例成员.

类成员,直接属于类, 可以通过类来访问static字段和static方法.
实例成员,只属于对象, 通过对象来访问非static字段和非static方法.
(对象其实可以访问类成员,但是底层依然使用类名访问的.)

在static方法中,只能调用static成员.

非static方法,可以访问静态成员,也可以访问实例成员.
什么时候定义成static的字段和方法:
如果这个一个状态/行为属于整个事物(类),就直接使用static修饰.
被所有对象所共享.

在开发中,往往把工具方法使用static修饰.
如果不使用static修饰,则这些方法属于该类的对象,我们得先创建对象再调用方法,在开发中工具对象只需要一份即可,可能创建N个对象,此时我们往往把该类设计为单例的.,但是还是有点麻烦.
所以,一般的,在开发中设计工具方法,为了调用简单,我们使用static修饰.

类成员的使用
利处:对对象的共享数据进行单独空间的存储,节省空间,没有必要每一个对象中都存储一份,可以直接被类名调用。
弊端:生命周期过长。
定义变量的语法:
数据类型 变量名 = 值;

变量根据在类中定义位置的不同,分成两大类:
成员变量: 全局变量/字段(Field),不要称之为属性(错误). 直接定义在类中,方法外面.
1):类成员变量. 使用static修饰的字段.
2):实例成员变量. 没有使用static修饰的字段.
局部变量: 变量除了成员变量,其他都是局部变量.
1): 方法内部的变量.
2):方法的形参.
3):代码块中的变量,一对{}.

变量的初始值:初始化才会在内存中开辟空间.
成员变量: 默认是有初始值的(看图)
局部变量: 没有初始值,所以必须先初始化才能使用.

变量的作用域:
变量根据定义的位置不同,也决定了各自的作用域是不同的. 看变量所在的那对{}.
成员变量: 在整个类中都有效.
局部变量: 在开始定义的位置开始,到紧跟着结束的花括号为止.

成员变量,可以先使用后定义,局部变量必须先定义而后才能使用.
在这里插入图片描述
变量的生命周期:
变量的作用域指的是变量的存在范围,只有在这个范围内,程序代码才能访问它。当一个变量被定义时,它的作用域就确定了:

变量的作用域决定了变量的生命周期,说明作用域不同,生命周期就不一样。
变量的生命周期指的是一个变量被创建并分配内存空间开始,到该变量被销毁并清除其所占内存空间的过程。
在这里插入图片描述
局部变量定义后,必须显示初始化后才能使用,因为系统不会为局部变量执行初始化操作。这就意味着,定义局部变量后,系统并未为这个变量分配内存空间。
直到程序为这个变量赋值时,系统才会为局部变量分配内存,并将初始值保存到该内存中。
局部变量不属于任何类或实例,因此它总是保存在其所在方法的栈内存中。
基本数据局部变量:直接把这个变量的值保存到该变量所对应的内存中。
引用数据局部变量:这个变量内存中存的是地址,通过该地址引用到该变量实际引用堆里的的对象。
栈内存中的变量无需系统垃圾回收,其往往随方法或代码块的运行结束而结束。
什么时候使用成员变量和局部变量:
1:考虑变量的生存时间,这会影响内存开销。
2:扩大变量作用域,不利于提高程序的高内聚。

开发中应该尽量缩小变量的作用范围,如此在内存中停留时间越短,性能也就更高。
不要动不动就使用static修饰,一般,定义工具方法的时候,static方法需要访问的变量,该变量属于类,此时才使用static修饰字段.
也不要动不动就使用成员变量,因为存在着线程不安全问题,能使用局部变量尽量使用局部变量.
生活中场景:
在我的电脑硬盘的某一个文件夹中里存有1000部片,我会在把新的电影存到该文件夹中,也可以看该文件夹中的任何电影.
可能出现的问题/操作不便:
问题1: 不能出现文件名和拓展名相同的文件.---->如何解决?
问题2: 想看片了,想从1000部中找到自己现在想看的某一部,存在特别麻烦.
解决方案:
可以在存储电影之前,可以按照电影的类型分类管理.
爱情/惊悚/科幻/动作/喜剧/历史/XX老师的/龙哥等,
但是,即使如此分类之后,也可能出现喜剧文件夹中存在500部,我们可以继续细分.

在开发中,我们存在几百上千个Java文件,如果所有的Java文件都在一个目录中,管理起来也很痛苦.
此时,我们可以使用生活中的解决方案,不过呢,此时在Java中,我们把这个特殊文件夹称之为包(package).
关键字:package ,专门用来给当前Java文件设置包名.
语法格式: package 包名.子包名.子子包; 必须把该语句作为Java文件中,并且是第一行代码(所有代码之前).
此时编译命令: javac -d . Hello.java
javac -d . Hello.java:
如果此时Hello.java文件中没有使用package语句,表示在当前目录中生成字节码文件.
如果此时Hello.java文件中使用了package语句,此时表示在当前目录中先生成包名,再在包中生成字节码文件.
运行命令: java 包名.类名;
package最佳实践 :
1):包名如何定义. 自定义的包名,不能以java.打头,因为Java的安全机制会检查.
1>:包名必须遵循标识符规范/全部小写.
2>:企业开发中,包名才有公司域名倒写.
小码哥域名: 520it.com ----->com.520it—>此时520it不符合标识符规范,我们可以使用下划线.(com.520it)
在Android中,如果package中使用了
,则不能部署到模拟器上.此时,我们也可以使用一个字母来代替_.
比如:package com.m520it; package com.a520it;
----------->
格式: package 域名倒写.模块名.组件名;
package com._520it.pss.util;----------------->进销存模块中专门存储工具类的包.

2):类的名称:
类的简单名称: 定义类的名称. PackageDemo.
类的全限定名称: 包名.类名; com._520it.hello.PackageDemo.

3):在开发中,都是先有package而后在package中再定义类.
在这里插入图片描述
当A类和B类不在同一个包中,若A类需要使用到B类,此时就得让A类中去引入B类.
注意:在这里演示import的时候,因为没有学习public的原因以及Editplus工具的原因,我不会演示去引入自定义带package的类.
我们演示,咱们之前学习过的 数组的工具类:Arrays类,该类在java.util包中.
Arrays类的全限定名:java.util.Arrays

没有使用import之前,操作不在同一个包中的类,得使用全限定名来操作.

解决方案: 使用import语句,直接把某个包下的类导入到当前类中.
语法格式: import 需要导入类的全限定名;
此后,在本Java文件中,只需要使用类的简单名称即可.

编译会自动去java.lang包中去寻找使用到的类,比如String,System.
所以他们不需要导入.
推论:非java.lang包的类,都需要导入.

上述代码,表示分别引入了java.util包下的Arrays类,Set类,List类.
问题:如果我们还需要继续引入java.util包中其他的类,我们还得提供N个import语句,要写很多次,不爽.
解决方案:使用通配符().
import 类的全限定名; 只能导入某一个类.
import 包名.子包名.
; 表示会引入该包下的所有的在当前文件中使用到的类.
import java.util.; 此时的表示类名.
在Eclipse工具中,即使我们使用了通配符*,在格式化代码的时候,也会转换为N条import语句.

注意: 编译器会默认找java.lang包下的类. 但是却不会去找java.lang的子包下的类.
           比如:java.lang.reflect.Method类.
           此时我们也得使用import java.lang.reflect.Method.

静态导入(static import):
在这里插入图片描述
在上述代码中,每次使用Arrays类中的静态方法,即使我们使用了import语句,但是每次都需要使用Arrays类名去调用静态方法.
我就觉得不爽, 我期望:我能把Arrays类中的静态成员作为自己的静态成员一样调用.
解决方案:
语法格式:
import static 类的全限定名.该类中的static成员名;
在这里插入图片描述
import static 类的全限定名.;此时的表示当前类的任意使用到的静态成员.
通过反编译工具,其实所谓的静态导入也是一个语法糖/编译器级别的新特性.
在实际开发中,我们不使用静态导入,因为如此,分不清某一个静态方法或字段来源于哪一个类.
Eclipse工具,当格式化代码的时候,就自动取消了所有的静态导入,变成使用类名调用.

人类的年龄状态能不能设置成负数?从设置数据的安全性引出封装。

什么是封装(面向对象三大特征之一)?
① 把对象的状态和行为看成一个统一的整体,将二者存放在一个独立的模块中(类);
② “信息隐藏”, 把不需要让外界知道的信息隐藏起来,尽可能隐藏对象功能实现细节,向外暴露方法,保证外界安全访问功能; 把所有的字段使用private私有化,不准外界访问,把方法使用public修饰,允许外界访问.
把所有数据信息隐藏起来,尽可能隐藏多的功能,只向外暴露便捷的方法,以供调用.

封装的好处:
1):使调用者正确,方便地使用系统功能,防止调用者随意修改系统属性.
2):提高组件的重用性.
3):达到组件之间的低耦合性(当某一个模块实现发生变化时,只要对外暴露的接口不变,就不会影响到其他模块).
通过什么来实现隐藏和暴露功能呢?

高内聚:把该模块的内部数据,功能细节隐藏在模块内部,不允许外界直接干预。
低耦合:该模块只需要给外界暴露少量功能方法。
封装其实就是要让有些类看不到另外一些类里面做了什么事情。所以java提供了访问权限修饰符来规定在一个类里面能看到什么,能暴露什么。

访问权限控制:
private: 表示私有的, 表示类访问权限. 只能在本类中访问,离开本类之后,就不能直接访问.
不写(缺省): 表示包私有,表示包访问权限. 访问者的包必须和当前定义类的包相同才能访问.
protected: 表示子类访问权限,同包中的可以访问,即使不同包,但是有继承关系,也可以访问.
public: 表示全局的,可以公共访问权限,如某个字段/方法,使用了public修饰,则可以在当前项目中任何地方访问.

在这里插入图片描述

一般的:字段都使用private修饰,表达隐藏,为了安全性.
拥有实现细节的方法,一般使用private修饰,不希望外界(调用者)看到该方法的实现细节.
一般的,方法我们使用public修饰,供外界直接调用.

//直接暴露给外界,供调用者直接调用即可
public void doWork()
{
methodA();
methodB();
methodC();
}
//仅仅只是完成了部分操作,不需要调用者调用
private methodA(){}
private methodB(){}
private methodC(){}

一般的,我们不用缺省,即使要使用,也仅仅是暴露给同包中的其他类.
protected:一般,在继承关系中,父类需要把一个方法只暴露给子类.
JavaBean规范:
JavaBean 是一种JAVA语言写成的可重用组件(类).
必须遵循一定的规范:
1):类必须使用public修饰.
2):必须保证有公共无参数构造器,即使手动提供了带参数的构造器,也得提供无参数构造器…
3):包含了属性的操作手段(给属性赋值,获取属性值).
分类:
1):复杂:UI,比如Button,Panel,Window类.
2):简单:domain,dao,service组件,封装数据,操作数据库,逻辑运算等.
(封装有字段,并提供getter/setter)
成员:
1):方法:Method
2):事件:event
3):属性:property

属性:
1):attribute:表示状态,Java中没有该概念,很多人把字段(Field)称之为属性(attribute).不要把成员变量叫做属性.
2):property: 表示状态,但是不是字段,是属性的操作方法(getter/setter)决定的,框架中使用的大多是是属性.

我们讲完封装之后,说应该把对象中信息隐藏起来(把类中的字段全部使用private修饰起来,其他类不能直接访问).
为了能让外界(其他类)访问到本类中的私有字段成员,我们专门提供getter以及setter方法.

字段: private String name;

getter方法: 仅仅用于获取某一个字段存储的值.
//去掉get,把首字母小写,得到name1.此时name1才是属性.
public String getName1()
{
return name;//返回name字段存储的值
}
如果操作的字段是boolean类型的,此时不应该叫做getter方法,而是is方法,把 getName 变成 isName.

setter方法: 仅仅用于给某一个字段设置需要存储的值.
public void setName1(String n)
{
name = n;//把传过来的n参数的值,存储到name字段中
}
每一个字段都得提供一对getter/setter.以后使用Eclipse工具之后getter/setter都是自动生成.

在JavaBean中有属性这个概念,只有标准情况下字段名和属性名才相同.
什么是this:
表示当前对象,什么优势当前对象.
this主要存在于两个位置:
构造器中: 就表示当前创建的对象.
方法中: 哪一个对象调用this所在的方法,那么此时this就表示哪一个对象.

当一个对象创建之后,JVM会分配一个引用自身的引用:this.
使用this:
① 解决成员变量和参数(局部变量)之间的二义性,必须使用;
② 同类中实例方法间互调(此时可以省略this,但是不建议省略)。
③ 将this作为参数传递给另一个方法;
④ 将this作为方法的返回值(链式方法编程);
⑤ 构造器重载的互调,this([参数])必须写在构造方法第一行;
⑥ static不能和this一起使用;
当字节码被加载进JVM,static成员以及存在了.
但是此时对象还没有创建,没有对象,就没有this.
当多个构造器重载时,或者多个方法重载时,一般的,我们是少参数的调用多参数的.

参数越多,该方法考虑的未知的因素也越多,也就是说功能更强大.

创建对象并给对象设置初始值有两种方式:
方式1:先通过无参数构造器创建出一个对象,再通过对象调用相应的setter方法.
方式2: 直接调用带参数的构造器,创建出来的对象,就有了初始值.

通过构造器和通过setter方法都可以完成相同的功能.
给对象设置数据:
1):setter注入(属性注入).
2):构造注入.

如何选择:
1:如果存在带参数的构造器,方式2是比较简洁的.
2:如果在构建对象的时候需要初始化多个数据,如果使用方式2,那么构造器得提供N个参数,参数过大,不直观,此时方式1,简单明了.
3:圆对象,如何画圆. 圆对象,必须根据半径来确定对象.
就应该在构建圆对象的时候,就要确定半径值.
有时候,需要根据数据来构建对象,此时优先选用构造器方式.

其他时候,任选.
什么是继承关系:
基于某个父类对对象的定义加以拓展,而产生新的子类定义,子类可以继承父类原来的某些定义,也可以增加原来父类所没有的定义,或者覆写父类中的某些特性。

从面向对象的角度上说:继承是一种从一般到特殊的关系,是一种“is a”的关系,即子类是对父类的拓展,是一种特殊的父类,比如:狗是动物的一种特殊情况,狗属于动物。

在Java语言中,存在多个类的时候,我们使用”extends”关键字来表示子类和父类之间的关系.
语法格式: 在定义子类的时候来表明自己需要拓展于哪一个父类.
public class 子类类名 extends 父类类名
{
编写自己特有的状态和行为
}

在Java中,类和类之间的继承关系只允许单继承,不允许多继承.
也就是说一个类A,只能有一个直接的父类,不能出现类A同时继承于
类B和类C.
但是,Java中允许多重继承.
多重继承例子:
动物有胎生动物和卵生动物之分,胎生动物有老虎,老虎又分华南虎,
东北虎,孟加拉虎等。


在Java中除了Object类之外,每一个类都有一个直接的父类.
比如:class Student extends Person{}
我们就说此时Student的直接父类是Person.
问题: class Person{} ,此时Person的父类又是谁?
Object类是Java语言的根类(老祖宗,任何类都是Object的子类.)
class Person{} 等价于 class Person extends Object{}
Object类要么是一个类直接父类,要么是一个类间接父类.

继承关系的作用:
1):解决了代码的重复问题.
2):真正的作用,表示出一个体系.
先写父类还是先写子类:
一般的,我们在开发工程中先编写多个自定义类,写完之后,发现多个类之间存在共同的代码,此时可以抽去出一个父类.
我们以后做开发,都是基于框架/组件来做的,我们是在别人的基础之上,继续做开发.
好比,别人提供清水房,我们只需要在清水房的基础之上装修,就可以使用.
以后,我们定义新的类去继承于框架中/组件中提供的父类.

子类继承父类之后,可以拥有父类的某一些状态和行为(子类复用了父类的功能或状态).
子类到底继承了父类的哪些成员(根据访问修饰符来判断):
1):如果父类中的成员使用public修饰,子类继承.
2):如果父类中的成员使用protected修饰,子类也继承,即使父类和子类不在同一个包中.
3):如果父类和子类在同一个包中,此时子类可有继承父类中 缺省修饰符的成员.
4):如果父类中的成员使用private修饰,子类打死都继承不到.因为private只能在本类中访问.
5):父类的构造器,子类也不能继承,因为构造器必须和当前的类名相同.

请注意:不要去背文字,立马写代码去证明.
方法覆写的原则(一同两小一大):Override
一同:
① 实例方法签名必须相同。 (方法签名= 方法名 + 方法的参数列表)
两小:
② 子类方法的返回值类型是和父类方法的返回类型相同或者是其子类。
子类可以返回一个更加具体的类.
③ 子类方法声明抛出的异常类型和父类方法声明抛出的异常类型相同或者是其子类。
子类方法中声明抛出的异常小于或等于父类方法声明抛出异常类型;
子类方法可以同时声明抛出多个属于父类方法声明抛出异常类的子类(RuntimeException类型除外);
一大:
④ 子类方法的访问权限比父类方法访问权 限更大或相等。
private修饰的方法不能被子类所继承,也就不存在覆盖的概念.

判断是否是覆写方法的必杀技:@Override标签:若方法是覆写方法,在方法前或上贴上该标签, 编译通过,否则,编译出错。
只有方法存在覆盖的概念,字段没有覆盖.
方法覆盖解决的问题: 当父类的某一个行为不符合子类具体的特征的时候,此时子类需要重新定义父类的方法,并重写方法体.
方法重载和方法覆盖(方法重写)的区别:
方法重载: Overload
方法重写: Override
批判,本身二者一点关系都没有,仅仅只是因为名字很像.

方法重载: Overload
作用: 解决了同一个类中,相同功能的方法名不同的问题.
既然是相同的功能,那么方法的名字就应该相同.
规则: 两同一不同.
同类中,方法名相同,方法参数列表不同(参数类型,参数个数,参数顺序).
方法重写: Override
作用:解决子类继承父类之后,可能父类的某一个方法不满足子类的具体特征,此时需要重新在子类中定义该方法,并重写方法体.
规则: 一同两小,一大.
一同:父类和子类的方法签名是相同的,所以,建议:直接拷贝父类中方法的定义到子类中,再重写方法体,就OK了.

子类初始化过程:创建子类对象的过程.

在创建子类对象之前,会先创建父类对象.
调用子类构造器之前,在子类构造器中会先调用父类的构造器,
默认调用的是父类无参数构造器…

1): 如果父类不存在可以被子类访问的构造器,则不能存在子类.
2):如果父类没有提供无参数构造器,此时子类必须显示通过super语句去调用父类带参数的构造器.
super关键字的使用场景:
1):可以使用super解决子类隐藏了父类的字段情况.该情况,我们一般不讨论,因为破坏封装.
2):在子类方法中,调用父类被覆盖的方法,引出super的例子,此时必须使用super.
3):在子类构造器中,调用父类构造器,此时必须使用super语句:super([实参]).

所谓隐藏就是“遮蔽”的意思。

① 满足继承的访问权限下,隐藏父类静态方法:若子类定义的静态方法的签名和超类中的静态方法签名相同,那么此时就是隐藏父类方法。注意:仅仅是静态方法,子类存在和父类一模一样的静态方法.

② 满足继承的访问权限下,隐藏父类字段:若子类中定义的字段和超类中的字段名相同(不管类型),此时就是隐藏父类字段,此时只能通过super访问被隐藏的字段。

③ 隐藏本类字段:若本类中某局部变量名和字段名相同,此时就是隐藏本类字段,此时只能通过this访问被隐藏的字段。

static不能和super以及this共存.

Object类的常见方法:
1): protected void finalize() :当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。
垃圾回收器在回收某一个对象之前,会先调用该方法,做扫尾操作. 该方法我们不要去调用.
2): Class getClass() :返回当前对象的真实类型。
3): int hashCode(): 返回该对象的哈希码值,hashCode决定了对象再哈希表中的存储位置,不同对象的hashCode是不一样的.
4): boolean equals(Object obj) :拿当前对象(this)和参数obj做比较.
在Object类中的equals方法,本身和 “ == ”符号相同,都是比较对象的内存地址.
官方建议:每个类都应该覆盖equals方法,不要比较内存地址,而去比较我们关心的数据.,因为我们关系的是内容数据,而不是内存地址.
比如:两个学生对象,我们不管是如何new出来的,只要学号相同我就应该认为是同一个对象.
两个字符串,只要内容相同,我们就认为是同一个字符串.
5):String toString():表示把一个对象转换为字符串.
打印对象时,其实打印的就是对象的toString方法.
System.out.println(obj对象);等价于 System.out.println(obj对象.toString());
默认情况下打印对象,打印的是对象的十六进制的hashCode值,但是我们更关系对象中存储的数据.
官方建议我们:应该每个类都应该覆盖toString,返回我们关心的数据.

学了继承关系,我们知道继承关系是一种”is A”的关系,也就说子类是父类的一种特殊情况
问题: 子类的对象是动物?
既然子类是一种特殊的父类,那么我们可不可以认为狗对象/猫对象就是动物类型的对象.
Animal d = new Dog(); //创建一只狗对象
Animal c = new Cat(); //创建一只猫对象

当我的代码变成以下的样子的时候,多态就产生了:
Animal a = new Dog();
对象(a)具有两种类型:
编译类型: 声明对象变量的类型,Animal,表示把对象看出什么类型.
运行类型: 对象的真实类型,Dog.运行类型—>对象的真实类型.
编译类型必须是运行类型的父类/或相同.
当编译类型和运行类型不同的时候,多态就出现了.
所谓多态: 对象具有多种形态,对象可以存在不同的形式.
Animal a = null;
a = new Dog(); //a此时表示Dog类型的形态
a = new Cat(); //a此时表示Cat类型的形态

多态的前提:可以是继承关系(类和类)/也可以是实现关系(接口和实现类),在开发中多态一般都指第二种.

我家里养了一只动物,名字叫”乖乖”,此时”乖乖”可以有多种形态;
乖乖 是狗, 乖乖的叫声: one one one.
乖乖 是猫, 乖乖的叫声: 喵 喵 喵.
多态的特点:
把子类对象赋给父类变量,在运行时期会表现出具体的子类特征(调用子类的方法).
Animal a = new Dog();

多态的好处:
需求:给饲养员提供一个喂养动物的方法,用于喂养动物.
没有多态:
发现,针对于不同类型的动物,我们得提供不同的feed方法来喂养.
不优雅.
我想,只提供一个方法,就能统一喂养所有动物.

存在多态:
统一了喂养动物的行为。
从上述例子,可以得知多态的作用:当把不同的子类对象都当作父类类型来看待,可以屏蔽不同子类对象之间的实现差异,从而写出通用的代码达到通用编程,以适应需求的不断变化。

基本数据类型转换:
自动类型转换: 把小类型的数据 赋给 大类型的变量. (此时的大和小表示的容量范围)
byte b = 12; byte是1个字节
int i = b; int是4个字节
强制类型转换: 把大类型的数据赋给 小类型的变量.
short s = (short) i ;short是2个字节

引用类型的转换:
引用类型的大和小,指的是 父类 和子类的关系.
自动类型转换: 把子类对象赋给父类变量(多态).
Animal a = new Dog();
Object是所有类的根类:
Object obj = new Dog();
强制类型转换: 把父类类型对象赋给子类类型变量(当时该父类类型变量的真实类型应该是子类类型).
Animal a = new Dog();
Dog d = (Dog)a;

instanceof 运算符: 判断该对象是否是某一个类的实例.
语法格式:boolean b = 对象A instanceof 类B; // 判断 A对象是否是 B类的实例,如果是,返回true.

instanceof运算符:
若对象是类的实例返回true,
若对象是类的父类的实例也返回true.

在开发中,有时候,我们只想判断是真实类型的实例,而不想判断为编译类型的实例.
System.out.println(obj instanceof Object);//true
System.out.println(obj instanceof String);//true

继承关系: 子类可以继承到父类中部分的成员,那么此时子类是可以修改到父类的信息的.
继承关系破坏封装,为了复用代码可能会让子类具有不该具有的功能.

为什么引入继承: 为了代码复用问题.
解决代码复用问题,不一定非要使用继承,也可以使用”包含关系”(has A).
我没钱,但是我想开豪车,我想吃火锅:
方式1: 任一个富豪干爹. 继承关系:
方式2: 把一个富豪绑架在我家里,挟天子以令诸侯! 组合关系/包含.

如果A类为了得到B的功能行为:
如果A类是B类的一种特殊情况,我们就应该采用继承来实现.

否则使用组合方式.

通过对象调用字段,在编译时期就已经决定了调用那一块内存空间的数据.
--------->字段不存在覆盖的概念,在多态时,不能有多态特征(在运行时期体现子类特征).

只有方法才有覆盖的概念.
当子类和父类存在相同的字段的时候,无论修饰符是什么(即使private),都会在各自的内存空间中存储数据.
什么是代码块:
在类或者在方法中,直接使用"{}"括起来的一段代码,表示一块代码区域。
代码块里变量属于局部变量,只在自己所在区域(前后的{})内有效。
根据代码块定义的位置的不同,我们又分成三种形式:
1):局部代码块:直接定义在方法内部的代码块:
一般的,我们是不会直接使用局部代码块的,只不过我们会结合if,while,for,try等关键字联合,表示一块代码区域.
2):初始化代码块(构造代码块):直接定义在类中.
每次创建对象的时候都会执行初始化代码块:
每次创建对象都会调用构造器,在调用构造器之前,会先执行本类中的初始化代码块.
通过反编译之后,我们发现,初始化代码也作为构造器的最初的语句.
我们一般不使用初始化代码块的,难看,即使要做初始化操作,我们一般在构造器中做即可,如果做初始化操作的代码比较多,此时构造器的结构比较混乱,此时专门定义一个方法做初始化操作,再在构造器中调用即可.
3):静态代码块:使用static修饰的初始化代码块.
在主方法执行之前执行静态代码块,而且只执行一次.
main方法是程序的入口,为什么静态代码块优先于main方法执行.
—>:静态成员随着字节码的加载也加载进JVM,此时main方法还没执行,因为方法需要JVM调用.
先把字节码加载进JVM, 而后JVM再调用main方法.
一般的,我们用来做初始化操作,加载资源,加载配置文件等.
为什么得需要使用final修饰符:
继承关系最大弊端是破坏封装:子类能访问父类的实现细节,而且可以通过方法覆盖的形式修改实现细节.

多个修饰符之间是没有先后关系的. public static final /public final static /final static punlic.

final本身的含义是“最终的,不可改变的”,它可以修饰非抽象类,非抽象方法和变量。注意:构造方法不能使用final修饰,因为构造方法不能被继承,肯定是最终的。
final修饰的类: 表示最终的类, 该类不能再有子类.
只要满足以下条件就可以把一个类设计成final类:
① 某类不是专门为继承而设计。
② 出于安全考虑,类的实现细节不许改动,不准修改源代码。
③ 确信该类不会再被拓展。
面试题:列举5个Java中内置的使用final修饰的类.
java里final修饰的类有很多,比如八大基本数据类型保证类和String等。

final修饰的方法: 最终的方法,该方法不能被子类覆盖.
什么时候的方法需要使用final修饰.
1):在父类中提供的统一的算法骨架,不准子类通过方法覆盖来修改. 此时使用final修饰.模板方法设计模式.
2):在构造器中调用的方法(初始化方法),此时一般使用final修饰.

注意: final修饰的方法,子类可以调用,但是不能覆盖.

final修饰的变量: 最终的变量,常量,该变量只能赋值一次,不能再赋值.
final是唯一可以修饰局部变量的修饰符.

final修饰的变量:表示常量,只能赋值一次,不能在赋值.

1):final变量必须显示地指定初始值,系统不会为final字段初始化。
2):final变量一旦赋予初始值,就不能被重新赋值。
3):常量名规范:常量名符合标识符,单词全部使用大写字母,如果是多个单词组成,单词间使用下划线隔开。
int类型的最大值: final int MAX_VALUE = …;
补充概念:全局静态常量: public static final 修饰的变量,直接使用类名调用即可.

面试题: final修饰的引用类型变量到底表示引用的地址不能改变,还是引用空间中的数据不能改变.
final修饰基本类型变量:表示该变量的值不能改变,即不能用“=”号重新赋值。
final修饰引用类型变量:表示该变量的引用的地址不能变,而不是引用地址里的内容不能变。

final是唯一可以修饰局部变量的修饰符,目的何在?期待局部内部类.局部内部类只能访问final修饰的局部变量
什么时候使用常量:
当在程序中,多个地方使用到共同的数据,且该数据不会改变,此时我们专门定义全局的常量,

一般的,我们在开发中会专门定义一个常量类,专门用来存储常量数据.

常量分类:
1):字面值常量(直接给出的数据值/直接量):比如:整数常量1,2,3,小数常量3.14,布尔常量false,true等。
2):定义的final变量: .

设计模式(Design pattern):是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的;设计模式使代码编制真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。

需求:定义一个数组的工具类(ArrayUtil),使用单例模式解决.
单例设计模式(singleton):最常用,最简单的设计模式.单例的编写有N种写法.
目的:保证某一个在整个应用中某一个类有且只有一个实例(一个类在堆内存只存在一个对象),即所有指向该类型实例的引用都指向同一块内存空间。
写单例模式的步骤:单讲饿汉式.
1):必须在该类中,自己先创建出一个对象.
2):私有化自身的构造器,防止外界通过构造器创建新的对象.
3):想外暴露一个公共的静态方法用于获取自身的对象.


工具类:存放了某一类事物的工具方法的类.
工具类存放的包:工具包(util,utils,tool/tools,helper,helpers),存放工具类.
工具类起名: XxxUtil/XxxUtils/XxxXTool/XxxTools.Xxx表示一类事物,
比如ArrayUtil,StringUtil,JdbcUtil.

工具类如何设计: 工具在开发中其实只需要存在一份即可.
1):如果工具方法没有使用static修饰,说明工具方法得使用工具类的对象来调用.
此时把工具类设计为单例的.
2):如果工具方法全部使用static修饰,说明工具方法只需要使用工具类名调用即可.
此时必须把工具类的构造器私有化(防止创建工具类对象调用静态方法).

一般的,首选第二种,简单. 在JDK中提供的工具类都是第二种. 如java.util.Arrays类.

引出基本类型包装类:
问题1:在面向对象中,”一切皆对象”,现在问题来了,int age = 17;请问这age代码哪里有对象,基本类型变量,不是对象.此时有矛盾.
问题2:现在给你一个复杂的十进制数据,请你迅速在程序给我转换为2进制,8进制,16进制,算法太麻烦了.
问题3:现在使用double类型来表示学生的考试成绩,double类型初始值为0,0:但是:你怎么表示一个人缺考和考试得0分.

上述的问题,进就是因为基本数据类型缺少对象,如果需要对象,必须先有类.
此时我们可以为每一个基本类型都编写一个对应的包装类,类中包含了该基本类型的一个值.

八大基本数据类型的包装类都使用final修饰,都是最终类,都不能被继承.
在这里插入图片描述
装箱:把基本类型数据转成对应的包装类对象。
拆箱:把包装类对象转成对应的基本数据类型数据。
Sun公司从Java5开始提供了的自动装箱(Autoboxing)和自动拆箱(AutoUnboxing)功能 :
自动装箱:可把一个基本类型变量直接赋给对应的包装类变量。
自动拆箱:允许把包装类对象直接赋给对应的基本数据类型变量。

自动装箱和拆箱,也是一个语法糖/编译器级别新特性.
在底层依然是手动装箱和拆箱操作.
但是:装箱操作使用的是Integer.valueOf的方式,而不是直接new Integer.

switch支持的数据类型:byte,short,char,int,也支持对应的包装类. 为何?
就是在底层,switch中会对包装类做手动拆箱操作.

在这里插入图片描述

解释Object obj = 17;
1):自动装箱: Integer i = 17;
2):引用的自动类型转换,把子类对象赋给父类变量: Object obj = i;

Object可以接受一切数据类型的值.
Object数组:Object[]该数组可以装一切数据类型.
Object[] arr = {“A”,12,3.14,true};

在这里插入图片描述
包装类的常用操作方法:
1:包装类中的常量:
MAX_VALUE/MIN_VALUE/SIZE(在内存中存储占多少位)/TYPE(对应的基本类型)
2:包装类的构造器,xxx类型的包装类Xxx:(xxx表示8大基本数据类型).
Xxx(xxx value):接受自己的基本类型值,如Integer(int val)/Boolean(boolean val)
Xxx(String value): 但是,Character除外.
构造器的作用:创建包装类对象.
3:基本类型和包装类型的转换(装箱和拆箱).
装箱:
Integer i1 = new Integer(123);
Integer i2 = Integer.valueOf(123);//推荐,带有缓存.
拆箱:
int val = i1.intValue();

4:String和基本类型/包装类型之间的转换操作:
String和int/Integer之间的转换操作:转换方法必须在String类中或Integer类中.

把String转换为包装类类型:
方式1: static Xxx  valueOf(String str)  :把String转换为包装类的对象
     Integer i1 = Integer.valueOf(“123”);
方式2: new  Xxx(String str):
     Integer i2= new Integer(“123”);
 把包装类对象转换为String.
    String  str =  任何对象.toString();
把基本数据类型转换为String:
    String  str = 17 + ””;
把String转换为基本数据类型:
  static xxx parseXxx(String s)      :xxx表示8大基本数据类型
  String  input=”12345”;
  int num = Integer.parseInt(input);

5:Boolean b = new Boolean(“SB”);//false
只认可true/TRUE,为true,其他都是false.

包装类中的缓存设计(享元模式),本质就是缓存设计:
Byte、Short、Integer、Long:缓存[-128,127]区间的数据;
Character:缓存[0,127]区间的数据;
包装类型对象之间的比较操作:统统使用equals方法来做比较,比较的是包装的数据.
Integer与int的区别(包装类型和基本数据类型的区别):
1):默认值:
int的默认值是0.
Integer的默认值为null.
推论:Integer既可以表示null,又可以表示0.
2):包装类中提供了该类型相关的很多算法操作方法.
static String toBinaryString(int i) :把十进制转换为二进制
static String toOctalString(int i) : :把十进制转换为八进制
static String toHexString(int i) : :把十进制转换为十六进制
3):在集合框架中,只能存储对象类型,不能存储基本数据类型值.

4):请问:Integer和int是相同的数据类型吗?不是:

5): 方法中的,基本类型变量存储在栈中,包装类型存放于堆中.

开发中,建议使用包装类型.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值