软件
- 软件的生命周期:软件的产生直到报废的整个过程。
1):问题的定义及规划: 此阶段是软件开发方与需求方共同讨论,主要确定软件的开发目标及其可行性。
2):需求分析: 在确定软件开发可行的情况下,对软件需要实现的各功能进行详细分析。需求分析阶段是一个很重
要的阶段,这一阶段做得好,将为整个软件开发项目的成功打下良好的基础。
3):软件设计: 此阶段主要根据需求分析的结果,把整个软件系统划分为大大小小的多个模块,设计出每一个模块
的具体结构。如系统框架设计,数据库设计等。软件设计一般分为总体设计和详细设计。
4):程序编码: 此阶段是将软件设计的结果转换成计算机可运行的程序代码。在程序编码中必须要制定统一,符合
标准的编写规范。以保证程序的可读性,易维护性,提高程序的运行效率。
5):软件测试: 在软件设计完成后要经过严密的测试,以发现软件在整个设计过程中存在的问题并加以纠正。整个
测试过程分单元测试(白盒)、集成测试(黑盒,功能测试、强度性能测试)以及系统测试三个阶段进行。测试的
方法主要有白盒测试和黑盒测试两种。在测试过程中需要建立详细的测试计划并严格按照测试计划进行测试,以减
少测试的随意性。
6):运行维护: 安装部署软件系统,修复软件中存在的bug和升级系统。在软件开发完成并投入使后,由于多方面
的原因,软件不能继续适应用户的要求。要延续软件的使用寿命,就必须对软件进行维护。软件的维护包括纠错性
维护和改进性维护两个方面。
- 软件的设计原则:
为了提高软件的开发效率,降低软件开发成本,一个优良的软件系统应该具有以下特点:
1,可重用性:遵循DRY原则,减少软件中的重复代码。
2,可拓展性:当软件需要升级增加新的功能,能够在现有的系统架构上方便地创建新的模块,而不需要改变软件现
有的结构,也不会影响以及存在的模块。
3,可维护性:当用户需求发生变化时,只需要修改局部的模块中的少量代码即可。
如何让软件系统达到上述的特点,我们对模块的要求:
1):结构稳定性:在软件设计阶段,把一个模块划分为更小的模块时,设计合理,使得系统结构健壮,以便适应
用户的需求变化。
2):可拓展性:当软件必须增加新的功能时,可在现有模块的基础上创建出新的模块,该模块继承了原有模块的
一些特性,并且还具有一些新的特性,从而实现软件的可重用和可拓展性。
3):可组合性:若干模块经过组合,形成大系统,模块的可组合性提高软件的可重用和可维护性,并且能简化软
件开发过程。
4):高内聚性:内聚,强调一个模块内的功能联系,每个模块只完成特定的功能,不同模块之间不会有功能的重
叠,高内聚性可以提高软件的可重用性和可维护性。
5):低耦合性:耦合,强调的是多个模块之间的关系,模块之间相互独立,修改某一个模块,不会影响到其他的
模块。低耦合性提高了软件的可维护性。
面向对象和面向过程
面向对象是相对面向过程而言,面向对象和面向过程都是一种思想
面向过程:强调的是功能行为,是一种比较早的思维方式,所有的事情都是以过程为角度出发思考的,强调的是功能,先干什么,在干什么,每一个功能即是一个函数,一步一步的执行,使用的时候调用函数即可
面向对象:将功能封装进对象,强调具备了功能的对象,整个程序的设计,都是以对象为参考点出发,例如会思考当前事物具备哪些功能和属性,通过当前事物本身出发功能来完成需求
- 示例1:
面向过程之洗袜子:1.倒一盆水,2.把臭袜子扔进去,3到洗衣粉,4,搓一搓晾起来
面向对象之洗袜子:1.虐汪版本:找一个对象(女朋友),帮你把袜子洗好就行
2.自虐版本:找一个对象(自己本身也是),你女朋把她的袜子给你你来完成
3.自力更生版本:找一个对象(洗衣机)
面向对象是基于面向过程的,能让复杂问题简单化,能让执行者转变成指挥者
- 示例2:
再例如:五子棋,面向过程的设计思路就是首先分析问题的步骤:1、开始游戏,2、黑子先走,3、绘制画面,4、判断输赢,5、轮到白子,6、绘制画面,7、判断输赢,8、返回步骤2,9、输出最后结果。把上面每个步骤用分别的函数来实现,问题就解决了。
面向对象的设计则是从另外的思路来解决问题。整个五子棋可以分为 1、黑白双方,这两方的行为是一模一样的,2、棋盘系统,负责绘制画面,3、规则系统,负责判定诸如犯规、输赢等。第一类对象(玩家对象)负责接受用户输入,并告知第二类对象(棋盘对象)棋子布局的变化,棋盘对象接收到了棋子的i变化就要负责在屏幕上面显示出这种变化,同时利用第三类对象(规则系统)来对棋局进行判定。
可以明显地看出,面向对象是以功能来划分问题,而不是步骤。同样是绘制棋局,这样的行为在面向过程的设计中分散在了总多步骤中,很可能出现不同的绘制版本,因为通常设计人员会考虑到实际情况进行各种各样的简化。而面向对象的设计中,绘图只可能在棋盘对象中出现,从而保证了绘图的统一。
- 百度百科:
面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。
面向对象就是是把构成问题事物分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。
面向对象
- 成员变量和局部变量:
作用范围:成员变量作用于整个类中,局部变量作用于函数或者语句中。
在内存中的位置:成员变量在堆内存中,因为对象的存在而存在;局部变量在栈内存中。
成员变量的初始值:数值类型 默认值是 0 ;boolean类型 默认是 false ;char类型 默认是一个空字符(不可见);引用类型 默认是 null ,成员变量中引用类型在没有初始化之前,尽量不要使用
- 匿名对象:
/*
* 匿名对象调用方法有意义,调用属性无意义
* 可以将匿名对象作为实际参数进行传值:print(new PetCat());
*/
public static void main(String[] args) {
new PetCat().age = 5;
new PetCat().name = "xiaobai";
//系统读到此句话时,第一句话已被废弃删除
new PetCat().PetCat();
//系统读到此句话时,第二句话已被废弃删除
}
- 类与对象:
将每个事物中共有的特征和行为抽取出来,形成一个描述,这个描述就是类。若需要描述类,就需要具体的事物,具体的事物就是对象(万物皆对象)。
类:
具有相同特征和行为的对象的抽象就是类,类就是对象的描述。因此,对象的抽象就是类,类的具体化就是对象。
类具有的特性:对对象的状态一种描述,一般使用的是成员变量。类具有的功能(行为):对象具体的行为,一般使用的是方法来描述。
ps:类是一组相关数据和方法的集合,所以类是引用类型(类也是封装)。
对象:
对象就是类所创建创建出来的具体事物(实例化),对象就具备着类中所提供的属性和行为。
出生:每次使用new关键字的时候,就会在堆内存空间中开辟新空间,此时对象就开始存在了。
死亡:当堆中的对象,没有任何变量的引用,此时对象就会成为垃圾,就会等待GC来回收,对象销毁后,会释放原有的空间。
- 类版本的方法传参:
public class StudentTest {
public static void main(String[] args) {
Student stu = new Student("李逵", 40);
System.out.println("过年前,学生的年龄是:"+stu.getAge());
/*
* 这样的传递方式是传值,并不是传地址,所以年龄是无法改变的
* 1.stu.getAge获取年龄的时候 返回的数据是 40 数据类型是int --> 值类型
* 2.addAge(int age)的参数类型是是int --> 值类型
* 所以整个过程都是在进行值传递
*/
addAge(stu.getAge());
System.out.println("过后前,学生的年龄是:"+stu.getAge());
System.out.println("过年前,学生的年龄是:"+stu.getAge());
/*
* 这样的传递方式是地址,并不是传值,所以年龄是可以改变的
* 1.因为Student是引用类型,并且方法参数类型就是Student所以是地址传递 --> 引用类型
* 2.因为age是封装到Student中,所有获取Student地址就相当于直接修改了参数值
* 所以整个过程都是在进行地址传递
*/
addAge(stu);
System.out.println("过后前,学生的年龄是:"+stu.getAge());
//地址传递 --> 修改姓名 String --> 引用数据类型
System.out.println("过年前,学生的姓名是:"+stu.getName());
changeName(stu.getName());
System.out.println("过后前,学生的姓名是:"+stu.getName());
}
/**
* 值:增加一岁
* @param age
*/
public static void addAge(int age){
age += 1;
}
/**
* 地址:增加一岁
* @param age
*/
public static void addAge(Student stu){
stu.setAge(stu.getAge() + 1);
}
/**
* 修改名字
* @param name
*/
public static void changeName(String name){
name = "李鬼";
}
}
运行结果:
- 包:
在工程中,没有创建包的情况下,默认会给一个默认包 default package
包名如何定义:1.包名不能以java开头,因为Java安全机制检查 2.包名全小写,当前公司域名的倒写作为包的开头
例如:www.xxx.com --> com.xxx 包名开头后后续写什么? --> com.xxx.工程的名字.包的作用.包细化的名字
封装
/*
* 封装:隐藏对象的属性和实现细节,仅对外提供公共访问方式
* 优点:将变化隔离,便于使用,提高重用性,提高安全性
* 封装原则:将不需要对外提供的内容都隐藏起来;把属性都隐藏,提供公共方法对其访问
* 方法(函数)本身就是一个最小的封装体,类,包也都是封装
*
* private:私有,(权限修饰符),用于修饰类中的成员(成员变量,成员函数)
* 私有只在本类中有效
* 将成员变量或成员函数私有化以后,类以外即使建立了对象也不能直接访问
* 若需要访问,就需要在类中提供访问方式(set(),get())
* 可以在访问方式中加入逻辑判断等语句提高代码健壮性
* 注:私有只是封装的一种表现形式
*/
- 构造函数:
访问权限修饰符有两个, public必须写 或修改为 private(单例)
/*
* 构造函数
* 特点:函数名与类名相同;不用定义返回值类型;不可以写return语句
* 作用:给对象进行初始化 / 创建对象时对,对对象中成员变量进行初始化
* () --> 要初始化的成员变量 { } --> 初始化成员变量
* 多个构造函数是以重载的形式存在的
*
* 对象一建立就会调用与之对应的构造函数
* 默认构造函数:当一个类中没有定义构造函数时,系统会默认给该类加一个空参数的构造函数
* 其权限和所属类一致,类(public/无修饰) --> 默认构造函数 (public/无修饰)
*
* 构造函数和一般函数:写法上有所不同,运行上也不同
* 构造函数:对象一建立就运行,给对象初始化
* 一般函数:对象调用才执行,给对象添加对象具备的功能
* 一个对象建立,构造函数只运行一次,而一般方法可以被该对象调用多次
*
* 构造函数私有化:
* 通过私有化该类的构造函数防止其他程序创建本类对象
*
* 构造代码块:
* 作用:给对象进行初始化
* 对象一建立就运行,而且优先于构造函数执行
* 构造代码块:给所有对象进行统一初始化
* 构造函数:给对应的对象初始化
* 即构造代码块中定义的是不同对象共性的初始化内容
*/
public class PetCat {
//属性
String name;
int age;
String gender;
//构造代码块
{
System.out.println("cat sleeping~");
}
//构造方法(无参)
//因已有自定义构造方法,本类无默认构造方法
void PetCat(){
//进行一些默认初始化
System.out.println("一只猫");
}
//构造方法(有参)
void PetCat(int id){
System.out.println("一只编号为"+id+"的猫");
}
//一般方法
void catEat(){
System.out.println("猫吃饭");
}
void catSleep(){
System.out.println("猫睡觉");
}
}
- this关键字:
/*
* this:代表本类的对象 --> 代表它所在函数所属对象的引用
* 简单说:哪个对象在调用this所在的函数,this就代表哪个对象
* 应用:当定义类中功能时,该函数内部要用到调用该函数的对象时,这时用this来表示这个对象,
* 但凡本类功能内部使用到了本类对象,都用this表示
*/
public class Studente {
private String name;
private int age;
public Studente() {
super();
}
public Studente(String name) {
this();
this.name = name;
}
public Studente(String name, int age) {
/*
* 用于构造函数之间互相调用
* 构造函数之间调用只能用this
* 只能定义在构造函数的第一行,因为初始化要先执行
*/
this(name);
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//在成员方法方法中使用this关键字
public void showInfos() {
System.out.println(this.name);
}
public void display() {
//this关键字也可以调用成员方法
this.showInfos();
}
}
this可以做为方法参数传递:(eg:String类底层源码)(使用场景较少)
- static关键字:
/*
* static:
* 修饰符 --> 修饰(成员变量,成员函数)
* 被修饰后的(成员变量,成员函数)除可被对象调用外,可直接被类名调用
*
* 特点:
* 1.随着类的加载而加载,属于类,而非对象,可通过类名调用
* 即静态会随着类的消失而消失,说明它的生命周期最长
* 2.优先于对象存在
* 静态先存在,对象后存在
* 3.被所有对象所共享
* 4.可直接被类名所调用
*
* 静态使用注意事项:
* 1.静态方法只能访问静态成员(变量,方法) --> 可以通过创建对象的方法间接调用
* 非静态方法既可访问静态也可访问非静态
* 2.静态中不可以定义this,super关键字
* 因静态优先于对象存在,所以不可出现this,super
*
* 利处:1.单独存储对象的共有数据,节省空间 2.可直接被类名调用
* 弊端:1.生命周期过长 2.访问出现局限性(静态虽好,只能访问静态)
*/
public class StaticDemo {
public static void main(String[] args) {
// 静态属性所有对象共享
System.out.println(Student.nameString);
Student.nameString = "小白";
System.out.println(Student.nameString);
// 创建两个对象
Student stu1 = new Student();
Student stu2 = new Student();
/*
* static优先于对象存在
* Java是允许使用对象.调用属性 不仅是成员变量 还可以是静态变量
* 但不要这样,需要区分成员变量和静态变量
*/
System.out.println(stu1.nameString);
stu1.nameString = "小黑";
System.out.println(stu1.nameString);
System.out.println(stu2.nameString);
}
}
class Student {
static String nameString = "小花";
}
运行结果:
- 静态代码块:
代码块种类:
1.静态代码块 --> 定义在类中的
2.局部代码块 --> 定义在类中或方法中
ps:定义在类中的话也可以叫做构造代码块(初始代码块)
构造代码块(初始化代码块)是构造方法前身
3.构造方法代码块 --> 定义在类中
三种代码块的执行过程:
静态代码块被调用 --> 构造代码块被调用 --> 构造方法代码块被调用
ps:静态代码块只会执行一次
class StaticTest{
static{
System.out.println("静态代码块执行");
}
{
System.out.println("构造代码块1执行");
}
{
System.out.println("构造代码块2执行");
}
public StaticTest() {
System.out.println("空参构造函数执行");
}
public StaticTest(String a) {
System.out.println("有参构造函数执行");
}
public static void staticMethod(){
System.out.println("静态方法执行");
}
public static void main(String[] args) {
new StaticTest().staticMethod();
}
}
运行结果:
- 对象的初始化及调用成员过程:
/*
* Person p = new Person("李明");
*
* 这句话都做了什么事情?
* 1.因为new用到了Person.class,所以会先找到Person.class文件加载到内存中
* 2.执行该类中的static代码块,给Person.class类进行初始化
* 3.在堆内存中开辟空间,分配内存地址
* 4.在堆内存中建立对象的特有属性,并进行默认初始化
* 5.对属性进行显示初始化
* 6.对对象进行构造代码块初始化
* 7.对对象进行对应的构造函数初始化
* 8.将内存地址赋给栈内存中的p变量
*/
- 工具类:
/*
* static应用:
* 对象的共享数据 --> 静态变量 ps:多线程并发访问临界资源的问题
* 功能内部没有访问对象的特有数据(非静态数据)时,
* 抽取共性功能独立封装 --> 静态函数
*
* 工具类:当类中用到工具类时,会先编译工具类文件,再编译自己
* 普通类:1.对象是用来封装特有数据的,该普通类并未封装特有数据
* 2.该普通类中每一个方法都未用到该类对象的特有数据
* 此时 该普通类 --> 工具类(每个方法都static)
* 更严谨:私有化构造函数,强制此类不能建立对象
*/
public class ArrayTools {
private ArrayTools() {}
/**
* 创建一个长度为n的随机数组,数值范围[0,100)
* @param n 长度(整数)
*/
public static int[] creatIntArr(int n){
int[] arr = new int[n];
for(int i = 0; i < arr.length; i++){
arr[i] = (int) (Math.random()*100);
}
return arr;
}
/**
* 将传入的数组倒序
* @param arr 数组(整数)
*/
public static void reverseOrder(int[] arr){
int[] arrTool = new int[arr.length];
int i;
for(i=0; i<arr.length; i++){
arrTool[i] = arr[i];
}
for(i=i-1; i>=0; i--){
arr[arr.length-i-1] = arrTool[i];
}
}
/**
* 打印传入的数组
* @param arr 数组(整数)
*/
public static void arrToStr(int[] arr){
System.out.print("[");
for(int i=0; i<arr.length; i++){
System.out.print(arr[i]);
if(i<arr.length-1){
System.out.print(",");
}else{
}
}
System.out.print("]");
}
}
一般是在工程中经常被使用到的一些方法,抽取出来封装成一个类,这个类中会提供对应的方法,每一个方法都是静态的,这个类就称为工具类。
名称:XXXUitl 或 XXXTools 这种类会写在对应的包中,包名也是 Utils 或 Tools。
ps:工具中只能写静态方法,属性也是静态属性并且不能构建对象。
继承
/*
* 子父类:
*
* 1.变量
* 子类出现非私有的同名成员变量时,访问本类变量用this,访问父类用super
* 因this代表本类对象的引用,super代表父类对象的引用
* 2.函数
* 子类 重写(覆盖) 父类函数后,运行子类函数的内容
* 重写:子类权限 >= 父类权限 静态只能覆盖静态
*
* 重载:同名函数,参数不同
* 重写:子父类相同方法(同名同参)
*
* 3.构造函数
* 初始化子类对象,父类的构造函数也会运行
* 因子类的构造函数默认第一行有一条隐式语句super();
* super():访问父类空参构造函数,一定定义在构造函数第一行
* 子类所有的构造函数默认第一行都是super();
* 为什么子类一定要访问父类中的构造函数:
* 因父类中的数据子类可以直接获取,所以子类对象建立时,需先查看父类是如何对这些数据进行初始化的
* 所以子类在初始化对象时,要先访问一下父类中的构造函数
* 如果要访问父类中指定的构造函数,可以通过手动定义super语句的方式来指定
* 结论:子类的所有构造函数,默认都会访问父类中空参数的构造函数,因为子类每个构造函数第一行的隐式super();
* 当父类中没有空参构造函数时,子类必须手动通过super指定要访问的父类构造函数
* 子类构造函数第一行也可以手动指定this访问本类中的构造函数,子类中至少有一个构造函数访问父类构造函数
*/
/*
* 继承:
* 子类是父类的一种扩展
* 父类中的私有属性,子类无法继承
* java支持单继承多实现,允许间接继承(多重继承)
*
* 创建自定义类时,没有指定继承于其他类默认继承于Object(根类)
*
* 优点:减少了代码的重复,让类与类之间形成一个体系
*
* 子类是可以继承父类的属性和方法的(公有)
* 子类是无法继承父类的构造方法的,所以子类必须提供自己的构造方法
*
* 重写要求:
* 1.必须有继承关系
* 2.实现方法的方法名签名必须一致 (方法签名= 方法名+参数列表)
* 3.子类的返回值类型可以和父类相同或其子类类型
* 4.子类方法声明抛出的异常类型和父类声明方法抛出异常类型相同或其子类
* 5.子类方法的权限比父类方法的权限要大或是相同
* 6.父类使用private 或final 或static 修饰的方法不能被子类重写
*
* 覆盖:
* 子类重写了父类的静态方法,父类调用时执行父类方法,子类调用时执行子类方法
*/
/*
* 创建一个Brid类提供一个方法fly方法
* 创建子类 麻雀Sparrow /鸵鸟 Ostich 继承父类,重写方法
*/
public class Demo {
public static void main(String[] args) {
Sparrow sparrow = new Sparrow();
Ostich ostich = new Ostich();
sparrow.fly();
ostich.fly();
}
}
//鸟类
class Bird{
public void fly(){
System.out.println("飞翔");
}
}
//麻雀
class Sparrow extends Bird{
@Override
public void fly() {
System.out.println("麻雀很快乐,会飞");
}
}
//鸵鸟
class Ostich extends Bird{
//这个@注解 --> jdk1.5 提示作用
@Override
public void fly() {
System.out.println("鸵鸟很悲哀,不会飞");
}
}
ps:父类方法如何实现 子类照抄-->方法体替换即可
- super关键字:
/*
* super:
* 调用父类的属性和方法:super.属性 super.方法
* 不能在static修饰的方法中使用
*
* super();只能在子类的构造方法中调用并且需要在第一句
* super(); --> 调用父类的构造方法进行属性初始化
* 只要是父类中属性在子类构造方法中都需要使用super()形式进行初始化
* 若发生继承,子类在构造方法中默认会调用父类的无参构造方法,所以父类必须提供无参构造方法
* 若不提供,需在子类中显式的指定调用父类的哪个构造方法
*/
super和this关键的使用类似:
this-->当前对象 谁调用谁就是当前对象
super-->看做是父类的对象
this可以在当前类中使用,super必须在子类中使用
static关键字不能和 super或this 共存
super.属性 或 super.方法 调用父类中成员变量和方法
super()调用父类的构造方法:
super() ---> 父类的无参构造方法
super(参数) --> 父类的有参构造方法
子类可以继承父类的属性,子类在初始化的时候需要初始化这些属性,若父类中将属性私有化,只能通过get或set来完成,在子类构造方法中只能通过set来进行赋值,不过这样做并不好 --> 会使用super()调用父类的构造方法。
public class Student {
private String name;
public Student() {
super();
}
public Student(String name) {
this();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class BigStudent extends Student{
private int age;
public BigStudent() {}
//只要是继承,子类在构造方法中需要将父类中的属性一同初始化
public BigStudent(String name,int age) {
super(name);
//子类特有属性
this.age = age;
}
}
- 子类的实例化过程
子类实例化过程 --> 一定是先调用父类的构造方法,然后再调用子类的构造方法
默认是调用父类无参构造方法,所以父类必须提供无参构造方法
若父类中没有提供无参构造方法,此时可以提供一个有参构造方法,在子类中明确调用
ps:创建父类的时候,将有参、无参构方法都提供即可
ps:一定会先创建父类对象,然后再子类对象(书上说的)
//父类
public class Animal {
String kind;
public Animal() {
System.out.println("父类的无参构造方法被调用了");
}
public Animal(String kind) {
System.out.println("父类的有参构造方法被调用了");
this.kind = kind;
}
}
//子类
class QQ extends Animal{
private String name;
public QQ() {
System.out.println("子类的无参构造方法被调用了");
}
public QQ(String kind,String name) {
super(kind);
this.name = name;
System.out.println("子类的有参构造方法被调用了");
}
}
public class Test {
public static void main(String[] args) {
//构建QQ对象
QQ mht = new QQ();
System.out.println("----------------下面是有参创建-----------------");
QQ mht2 = new QQ("xxx","ooo");
}
}
运行结果:
- 面试题:
public class Test {
public static void main(String[] args) {
Man man = new Man();
}
}
class Person{
static{
System.out.println("父类的静态代码块被调用");
}
{
System.out.println("父类的初始化代码块被调用");
}
public Person(){
System.out.println("父类的构造代码块被调用");
}
}
class Man extends Person{
static{
System.out.println("子类的静态代码块被调用");
}
{
System.out.println("子类的初始化代码块被调用");
}
public Man(){
System.out.println("子类的构造代码块被调用");
}
}
运行结果:
- final修饰符:
继承打破了封装性,final为此而存在
/*
* final修饰类 --> 终类 不能被继承
*/
public final class Woman {
/*
* fianl修饰属性 --> 全局常量,需要给值
* fianl修饰的变量一定要大写,并且多单词下划线分割
* final修饰的变量 就会变成常量 一旦赋值无法修改
*/
final String FOOD = "卫龙辣条";
//静态全局常量 fianl和static前后无所谓
final static String NAME = "老干妈";
public void finalShow1() {
System.out.println(FOOD);
//局部常量
final int age = 20;
}
public void finalShow2() {
//局部常量,可以先声明在赋值,不能二次赋值
final int age;
age = 20;
}
//final修饰方法
/**
* 可以继承,但是不能重写
* 父类所提供的方法,只能调用不能重写就用final
*/
public final void display() {
System.out.println("final方法");
}
}
多态
同一个操作被不同的对象所触发,产生的结果是不一样的。
多态分为两种:
方法多态---> 重载
对象多态--> 必须是继承
/*
* 多态作用:
* 当把不同的子类对象当做父类类型来看待时,可以屏蔽不同子类对象之间的差异,从而可以写出通用的代码(通用编程),
* 以适应不同需求的不断变化,从而达到减少代码的作用
*
* 对象的向上和向下转型(里氏转换原则):
* 对象向上转型:子类对象可以赋值给一个父类的引用,会自动将子类类型提升为父类类型
* 语法:父类类型 引用 = new 子类(); 即父类的引用可以得到一个子类的对象
* 特点:此时,该引用只能调用父类中的属性和行为,不能调用子类的特有属性和行为
* ps:若子类中重写父类的方法,调用时走的是子类的方法
* 多用于:作为方法的参数,构建接口对象
* 对象向下转型:将提升为父类类型的引用强制转换为原有子类的类型
* 语法: 子类类型 对象 = (子类类型)提升后的父类引用;
* 特点:此时,既可以调用子类的特有属性和形式,也可以调用父类中所提供的属性和行为
* 多用于:在方法中参数是父类类型,但是方法体的实现需要使用到子类的特有属性和行为
*/
/*
* 设计一个员工类,其中有计算员工薪水的方法
* 根据员工的不同,薪水计算的方式也不同 员工类型 薪水构成
* 经理 底薪+奖金
* 销售人员 底薪+提成
* 普通员工 底薪+补贴
* 创建一个执行类,场景年终,领导找员工谈话,让员工介绍自己的薪水(方法)
*/
public class Staff {
private String name;
private int salary;
public Staff() {
super();
}
public Staff(String name, int salary) {
super();
this.name = name;
this.salary = salary;
}
//计算薪水
public void addMoney(){
System.out.println("薪水是:"+this.salary);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
}
class Manager extends Staff{
private int reward;
public Manager() {
super();
}
public Manager(String name,int salay,int reward) {
super(name,salay);
this.reward = reward;
}
@Override
public void addMoney() {
System.out.println("薪水是:"+(getSalary()+this.reward));
}
@Override //此处重写了Object类的toString方法
public String toString() {
return "姓名:"+getName()+"\t底薪:"+getSalary()+"\t奖金:"+reward;
}
public int getReward() {
return reward;
}
public void setReward(int reward) {
this.reward = reward;
}
}
class Salesman extends Staff{
private int percentage;
public Salesman() {
super();
}
public Salesman(String name, int salary,int percentage) {
super(name,salary);
this.percentage = percentage;
}
@Override
public void addMoney() {
System.out.println("薪水是:"+(getSalary()+percentage));
}
@Override
public String toString() {
return "姓名:"+getName()+"\t底薪:"+getSalary()+"\t提成:"+percentage;
}
public int getPercentage() {
return percentage;
}
public void setPercentage(int percentage) {
this.percentage = percentage;
}
}
class Employee extends Staff{
private int subsidy;
public Employee() {
super();
}
public Employee(String name, int salary,int subsidy) {
super(name,salary);
this.subsidy = subsidy;
}
@Override
public void addMoney() {
System.out.println("薪水是:"+(getSalary()+subsidy));
}
@Override
public String toString() {
return "姓名:"+getName()+"\t底薪:"+getSalary()+"\t补贴:"+subsidy;
}
public int getSubsidy() {
return subsidy;
}
public void setSubsidy(int subsidy) {
this.subsidy = subsidy;
}
}
//测试类
public class Test {
public static void main(String[] args) {
Manager manager = new Manager("张三",8000,2000);
Salesman salesman = new Salesman("李四",4000,2000);
Employee employee = new Employee("赵六",6000,1000);
Test test = new Test();
test.show();
test.showInfosManager(manager);
test.showInfosSalesman(salesman);
test.showInfosEmployee(employee);
System.out.println("-------------------使用多态之后-----------------");
/*
* showInfosEmployee(Employee e)
* Employee e = new Manager();
*/
test.showInfosStaff(manager);
test.showInfosStaff(salesman);
test.showInfosStaff(employee);
}
public void show() {
System.out.println("场景年终,领导找员工谈话,让员工介绍自己的薪水");
}
//现在只有三个员工,只要写三个方法打印就可以
//如果员工暴增10000人,介绍员工怎么写?
public void showInfosManager(Manager manager) {
System.out.println(manager);
System.out.print("总薪水:");
manager.addMoney();
}
public void showInfosSalesman(Salesman salesman) {
System.out.println(salesman);
System.out.print("总薪水:");
salesman.addMoney();
}
public void showInfosEmployee(Employee employee) {
System.out.println(employee);
System.out.print("总薪水:");
employee.addMoney();
}
//根据暴增的需求,可以使用多态解决
//同一个事物被不同对象所触发的到结果是一样
/*
* 里氏转换原则
* 1.父类对象可以接受一个子类的引用(向上转型)
*/
public void showInfosStaff(Staff staff) {
System.out.println(staff);
System.out.print("总薪水:");
staff.addMoney();
}
}
- instanceof:
/*
* 进行向下转型的时候:
* 需要注意一个问题,可能会出现提升之后的对象和要转换成子类,但类型不匹配
* 这个在编译阶段是不会报错,只有运行时才会出现异常ClassCastException(类型转换异常)
* 为了避免这样的事情发生,用一个关键字 instanceof 进行判断
* 语法:提升之后的对象 instanceof 子类类型 (true当前对象就是要转换的类型,false不是)
*
*/
/*
* 设计一个动物类,属性: 姓名,性别
* 设计一个猫类,设计一个狗类, 猫和狗都继承自动物类
*
* 需求:在测试类中设计一个方法,这个方法需要一个参数,参数的类型可以是猫类,也可以是狗类 -->多态(动物类)
* 在方法体内,将传入的猫或者狗的属性输出即可
* 输出到底是猫还是狗
*/
public class Test {
public static void main(String[] args) {
Cat cat = new Cat("小白", "加菲猫");
Dog dog = new Dog("小黑","拉布拉多");
checkAnimal(cat);
checkAnimal(dog);
}
public static void checkAnimal(Animal animal) {
if(animal instanceof Dog) {
Dog dog = (Dog)animal;
System.out.println(dog);
}else {
Cat cat = (Cat)animal;
System.out.println(cat);
}
}
}
class Animal {
private String name;
private String kind;
public Animal() {
super();
}
public Animal(String name, String kind) {
super();
this.name = name;
this.kind = kind;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getKind() {
return kind;
}
public void setKind(String kind) {
this.kind = kind;
}
}
class Cat extends Animal{
public Cat() {
super();
}
public Cat(String name, String kind) {
super(name, kind);
}
@Override
public String toString() {
return "我是Cat类对象";
}
}
class Dog extends Animal{
public Dog() {
super();
}
public Dog(String name, String kind) {
super(name, kind);
}
@Override
public String toString() {
return "我是Dog类对象";
}
}
- 动态绑定:
/*
* 动态绑定:
* 向上转型后无论子类中是否有与父类重名的属性,都只会调用父类的属性
* 只有当子类重写父类的方法时,父类才会调用重写的方法,若没有任何重写,只会调用父类中的方法
*/
public class Test {
public static void main(String[] args) {
//实现多态的向上转型
Person p = new Man();
System.out.println(p.num);
p.show();
//实现多态的向下转型
Man man = (Man)p;
System.out.println(man.num);
man.show();
}
}
class Person {
public int num = 10;
public void show() {
System.out.println("父类中的show方法");
}
public void display() {
System.out.println("父类中的display方法");
}
}
class Man extends Person{
public int num = 20;
public void show() {
System.out.println("子类中的show方法");
}
public void showInfos() {
System.out.println("子类中的showInfos方法");
}
}
运行结果:
访问权限修饰符
修饰类:只有两个权限 public 或 default(Package)
public:公有权限,在工程任意位置都可以访问
default(Package) 只能在当前包内访问
private protected:私有保护成员。可被两种类使用。分别是:该类本身和该类的所有子类。