(二十五)基础系列 面向对象
来源:互联网 发布:小米换苹果 迁移数据 编辑:程序博客网 时间:2024/06/08 16:13
什么是对象,就是现实具体事物的描述。
一、自定义类
* A: 数据类型 * a: java中的数据类型分为:基本类型和引用类型* B: 引用类型的分类 * a: Java为我们提供好的类,比如说:Scanner,Random等。 * b: 我们自己创建的类,按照类的定义标准,可以在类中包含多个方法与属性,来供我们使用。 * A: 自定义类的概述 * java代码映射成现实事物的过程就是定义类的过程。 * 举例: 我们就拿一部手机进行分析,它能用来做什么呢?它可以打电话,上网,聊微信等,这些就是手机所提供的功能,也就是方法;手机也有它的特征,如颜色、尺寸大小、品牌型号等,这些就是手机的特征,也就是属性 * 目前,我们只关注类中的属性,类中的方法在面向对象部分再进行学习。* A: 自定义类的格式 * a: 使用类的形式,对现实中的事物进行描述。 * b: 事物由方法和属性两部分组成。 * 方法: 这个事物具备的功能。 * 属性: 这个事物具备的特征。 * c: 格式 public class 类名{ 属性定义 修饰符 数据类型 变量名 = 值 方法定义 修饰符 返回值类型 方法名(参数列表){ } }
二、类的调用演示* A: 自定义的手机类 * a: 案例代码 public class Phone{ /* 定义手机的属性 */ String color ; String brand ; double size ; }* A: 调用方法执行流程 * a: 实现引用类型的步骤 * 1: 导入包 , 类都是在同一个文件夹,不需要导入包 * 2: 创建引用类型的变量 * 3: 变量.类型中的功能 * b: 案例代码 public class TestPhone{ public static void main(String[] args){ // 2: 创建引用类型的变量 Phone p = new Phone(); //System.out.println(p); //输出内存的地址 //3: 变量.类型中的功能 //变量 p.的方式,调用类中的属性 //属性就是变量 , 赋值和获取值 p.color = "土豪金"; p.brand = "爱立信"; p.size = 5.0; //获取属性值 System.out.println(p.color+" "+p.brand+" "+p.size); } }
三、类的内存分布三、List 集合
* A: ArrayList的常见方法 * a: add(参数) 向集合中添加元素 * b: get(int index) 取出集合中的元素,get方法的参数,写入索引 * c: size() 返回集合的长度, 集合存储元素的个数* B: 案例代码 import java.util.ArrayList; public class ArrayListDemo_1{ public static void main(String[] args){ //定义集合,存储字符串元素 ArrayList<String> array = new ArrayList<String>(); //调用集合方法add存储元素 array.add("abc"); array.add("itcast"); array.add("love"); array.add("java"); //输出集合的长度,调用集合方法size, size方法的返回值类型 int int size = array.size(); System.out.println(size); //获取出集合中的一个元素,获取1索引的元素 //集合的方法get, 获取元素后结果数据类型 String s = array.get(1); System.out.println(s); System.out.println(array.get(0)); System.out.println(array.get(1)); System.out.println(array.get(2)); System.out.println(array.get(3)); } }* A: ArrayList补充方法 * a: add(int 索引,存储的元素) 将元素添加到指定的索引上 * b: set(int 索引,修改后的元素) 将指定索引的元素,进行修改 * c: remove(int 索引) 删除指定索引上的元素 * d: clear() 清空集合中的所有元素* B: 案例代码 import java.util.ArrayList; public class ArrayListDemo_3{ public static void main(String[] args){ ArrayList<Integer> array = new ArrayList<Integer>(); array.add(1); array.add(2); array.add(3); array.add(4); //在索引2上,添加元素7 array.add(2,7); //将0索引上的元素,修改成10 array.set(0,10); //将4索引上的元素,删除 array.remove(4); array.clear(); //使用方法 size+get组合进行遍历 for(int i = 0 ; i < array.size(); i++){ System.out.println( array.get(i) ); } } }
四、List集合遍历
* A: 案例代码 /* 集合的遍历 实现思想也是索引思想 集合的索引从0开始,到 size()-1 方法get(int index) */ import java.util.ArrayList; public class ArrayListDemo_2{ public static void main(String[] args){ ArrayList<Integer> array = new ArrayList<Integer>(); array.add(121); array.add(125); array.add(123); array.add(120); array.add(128); //对集合进行遍历 //使用方法 size+get组合进行遍历 for(int i = 0 ; i < array.size(); i++){ System.out.println( array.get(i) ); } } }
五、选择排序* A: 案例代码 /* 数组的排序: 一般都是升序排列,元素,小到大的排列 两种排序的方式 选择排序: 数组的每个元素都进行比较 冒泡排序: 数组中相邻元素进行比较 规则: 比较大小,位置交换 */ public class ArrayMethodTest_2{ public static void main(String[] args){ int[] arr = {3,1,4,2,56,7,0}; //调用选择排序方法 //selectSort(arr); printArray(arr); } /* 定义方法,实现数组的选择排序 返回值: 没有 参数: 数组 实现步骤: 1.嵌套循环实现排序 外循环,控制的是一共比较了多少次 内循环,控制的是每次比较了多少个元素 2. 判断元素的大小值 小值,存储到小的索引 */ public static void selectSort(int[] arr){ for(int i = 0 ; i < arr.length - 1; i++){ //内循环,是每次都在减少,修改变量的定义 for(int j = i+1 ; j < arr.length ; j++){ //数组的元素进行判断 if(arr[i] > arr[j]){ //数组的换位 int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } } } /* 定义方法,实现功能 返回值: void 方法参数: 数组 */ public static void printArray(int[] arr){ //输出一半中括号,不要换行打印 System.out.print("["); //数组进行遍历 for(int i = 0 ; i < arr.length ; i++){ //判断遍历到的元素,是不是数组的最后一个元素 //如何判断 循环变量 到达 length-1 if( i == arr.length-1 ){ //输出数组的元素和] System.out.print(arr[i]+"]"); }else{ //不是数组的最后一个元素,输出数组元素和逗号 System.out.print(arr[i]+","); } } System.out.println(); } }
六、冒泡排序* A: 冒泡排序功能实现 * a: 题目分析 * 通过观察发现,本题目要实现把数组元素{13,46,22,65,3}进行排序 * 提到数组排序,就要进行元素值大小的比较,通过上图发现,我们想完成排序要经过若干次的比较才能够完成。 * 上图中相邻的元素值依次比较,把大的值放后面的元素中,数组循环一圈后,则把最大元素值互换到了最后一个元素中。 数组再循环一圈后,把第二大的元素值互换到了倒数第二个元素中。按照这种方式,数组循环多圈以后, 就完成了数组元素的排序。这种排序方式我们称为冒泡排序。 * b: 解题步骤 * 使用for循环(外层循环),指定数组要循环的圈数(通过图解可知,数组循环的圈数为数组长度 - 1) * 在每一圈中,通过for循环(内层循环)完成相邻的元素值依次比较,把大的值放后面的元素中 * 每圈内层循环的次数,由第几圈循环来决定。如上图所示 * 进行第一圈元素比较时,内层循环次数为数组长度 - 1 * 进行第二圈元素比较时,内层循环次数为数组长度 - 2 * 依次类推,得出结论:进行第n圈元素比较时,内层循环次数为数组长度 - n * c: 案例代码 /* 数组的排序: 一般都是升序排列,元素,小到大的排列 两种排序的方式 选择排序: 数组的每个元素都进行比较 冒泡排序: 数组中相邻元素进行比较 规则: 比较大小,位置交换 */ public class ArrayMethodTest_2{ public static void main(String[] args){ int[] arr = {3,1,4,2,56,7,0}; //调用选择排序方法 //selectSort(arr); //调用冒泡排序方法 bubbleSort(arr); printArray(arr); } /* 定义方法,实现数组的冒泡排序 返回值: 没有 参数: 数组 */ public static void bubbleSort(int[] arr){ for(int i = 0 ; i < arr.length - 1; i++){ //每次内循环的比较,从0索引开始, 每次都在递减 for(int j = 0 ; j < arr.length-i-1; j++){ //比较的索引,是j和j+1 if(arr[j] > arr[j+1]){ int temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } } /* 定义方法,实现功能 返回值: void 方法参数: 数组 */ public static void printArray(int[] arr){ //输出一半中括号,不要换行打印 System.out.print("["); //数组进行遍历 for(int i = 0 ; i < arr.length ; i++){ //判断遍历到的元素,是不是数组的最后一个元素 //如何判断 循环变量 到达 length-1 if( i == arr.length-1 ){ //输出数组的元素和] System.out.print(arr[i]+"]"); }else{ //不是数组的最后一个元素,输出数组元素和逗号 System.out.print(arr[i]+","); } } System.out.println(); } }
七、数组对半查找* A: 案例代码 /* 数组的查找功能 在一个数组中,找一个元素,是否存在于数组中,如果存在,就返回索引 普通查询: 找到元素在数组中出现的索引,如果没有这个 元素,结果就是负数 */ public class ArrayMethodTest_3{ public static void main(String[] args){ int[] arr = {1,3,5,7,9,11,15}; int index = binarySearch(arr,10); System.out.println(index); } /* 定义方法,实现,折半查找 返回值: 索引 参数: 数组,被找的元素 实现步骤: 1. 需要的变量定义 三个,三个指针 2. 进行循环折半 可以折半的条件 min <= max 3. 让被找元素,和中间索引元素进行比较 元素 > 中间索引 小指针= 中间+1 元素 < 中间索引 大指针= 中间-1 元素 == 中间索引 找到了,结束了,返回中间索引 4. 循环结束,无法折半 元素没有找到 ,返回-1 */ public static int binarySearch(int[] arr, int key){ //定义三个指针变量 int min = 0 ; int max = arr.length -1 ; int mid = 0; //循环折半,条件 min<=max while( min <= max){ //公式,计算中间索引 mid = (min+max)/2; //让被找元素,和中间索引元素进行比较 if(key > arr[mid]){ min = mid + 1; }else if (key < arr[mid]){ max = mid - 1; }else{ //找到元素,返回元素索引 return mid; } } return -1; } /* 定义方法,实现数组的普通查询 返回值: 索引 参数: 数组, 被找的元素 实现步骤: 1. 遍历数组 2. 遍历过程中,使用元素和数组中的元素进行比较 如果相同,返回元素在数组中的索引 如果不同,返回负数 */ public static int search(int[] arr, int key){ //遍历数组 for(int i = 0 ; i < arr.length ; i++){ //数组元素,被查找的元素比较 if(arr[i] == key){ //返回索引 return i; } } return -1; } }
八、Eclipse 断点调试* A:断点调试(又称为Debug调试)的作用 * 调试程序 * 查看程序执行流程* B:如何查看程序执行流程 * 什么是断点: * 就是一个标记,从哪里开始。 * 如何设置断点: * 你想看哪里的程序,你就在那个有效程序的左边双击即可。 * 在哪里设置断点: * 哪里不会点哪里。 * 目前:我们就在每个方法的第一条有效语句上都加。 * 如何运行设置断点后的程序: * 右键 -- Debug as -- Java Application * 看哪些地方: * Debug:断点测试的地方 * 在这个地方,记住F6,或者点击也可以。一次看一行的执行过程。 * Variables:查看程序的变量变化 * ForDemo:被查看的源文件 * Console:控制台 * 如何去断点: * a:再次双击即可 * b:找到Debug视图,Variables界面,找到Breakpoints,并点击,然后看到所有的断点,最后点击那个双叉
九、Eclipse 快捷键 * A: Eclipse的快捷键
* a: 内容辅助键 Alt+/
* 自动补齐main方法 main 然后 Alt+/
* 自动补齐输出语句 syso 然后 Alt+/
* b: 格式化代码
* Ctrl+Shift+f
* 代码区域右键 -- Source – Format
* c: 自动导包
* Ctrl+Shift+o
* 如果当前类在多个包中都存在,这时候,使用Ctrl+shift+o,进行选择一个包导入即可。
* d: 注释
* 单行注释
* 加注释: 先选中需要注释的内容,然后 Ctrl+/
* 取消注释:先选中需要取消注释的内容, 然后 Ctrl+/
* 多行注释
* 加注释: 先选中需要注释的内容,然后 Ctrl+Shift+/
* 取消注释:先选中需要取消注释的内容, 然后 Ctrl+Shift+\
* e: 补充
* 代码上下移动
* 选中代码alt+上/下箭头
* 查看源码
* 选中类名(F3或者Ctrl+鼠标点击)
* 查找具体的类
* ctrl + shift + t,输入要查找的类的名称-->确定
* 查找具体类的具体方法
* ctrl + o
* 给建议
* ctrl+1,根据右边生成左边的数据类型,生成方法
* 删除代码
* ctrl + d
* 抽取方法
* alt + shift + m
* 改名
* alt + shift + r(类名,方法名,变量名)
十、面向对象
* A: 类和对象的关系 * 类是对某一类事物的抽象描述,而对象用于表示现实中该类事物的个体* B: 举例 * 可以将玩具模型看作是一个类,将一个个玩具看作对象,从玩具模型和玩具之间的关系便可以看出类与对象之间的关系
* A: 需求:把大象装冰箱里 * a: 面向过程 * 自己打开冰箱门 * 自己将大象装进去 * 自己关闭冰箱门 * b: 面向对象 * 分析发现打开、装、关闭都是冰箱的功能。即冰箱对象具 备如下功能 * 冰箱打开 * 冰箱存储 * 冰箱关闭* B: 通过伪代码描述大象和冰箱 * 描述大象: class 大象 { } * 描述冰箱 class冰箱 { void 打开(){} void 存储(大象){} void 关闭(){} }* C: 使用对象: * 1、创建冰箱的对象 * 冰箱 bx = new 冰箱(); * 2、调用冰箱的功能 * 对象.功能(); * bx.打开(); * bx.存储(new 大象()); * bx.关闭();* D:总结: * 1、先按照名词提炼问题领域中的对象 * 2、对对象进行描述,其实就是在明确对象中应该具备的属性和功能 * 3、通过new的方式就可以创建该事物的具体对象 * 4、通过该对象调用它以后的功能。
十一、对象的内存图十二、成员变量和局部变量的区别
- 区别一:定义的位置不同
- 定义在类中的变量是成员变量
- 定义在方法中或者{}语句里面的变量是局部变量
- 区别二:在内存中的位置不同
- 成员变量存储在对内存的对象中
- 局部变量存储在栈内存的方法中
- 区别三:声明周期不同
- 成员变量随着对象的出现而出现在堆中,随着对象的消失而从堆中消失
- 局部变量随着方法的运行而出现在栈中,随着方法的弹栈而消失
- 区别四:初始化不同
- 成员变量因为在堆内存中,所有默认的初始化值
- 局部变量没有默认的初始化值,必须手动的给其赋值才可以使用。
* A.面向对象三大特征 * 封装、继承、多态* B.封装表现 * 1、方法就是一个最基本封装体 * 2、类其实也是一个封装体 * C.封装的好处 * 1、提高了代码的复用性 * 2、隐藏了实现细节,还要对外提供可以访问的方式。便于调用者的使用。这是核心之一,也可以理解为就是封装的概念 * 3、提高了安全性 * A.基本类型 class Demo { public static void main(String[] args) { int x = 4; show(x); System.out.println("x="+x); } public static void show(int x) { x = 5; } } 基本类型作为参数传递时,其实就是将基本类型变量x空间中的值复制了一份传递给调用的方法show(),当在show()方法中x接受到了复制的值,再在show()方法中对x变量进行操作,这时只会影响到show中的x。当show方法执行完成,弹栈后,程序又回到main方法执行,main方法中的x值还是原来的值。* B.引用类型 class Demo { int x ; public static void main(String[] args) { Demo d = new Demo(); d.x = 5; show(d); System.out.println("x="+d.x); } public static void show(Demo d) { d.x = 6; } } 当引用变量作为参数传递时,这时其实是将引用变量空间中的内存地址(引用)复制了一份传递给了show方法的d引用变量。这时会有两个引用同时指向堆中的同一个对象。当执行show方法中的d.x=6时,会根据d所持有的引用找到堆中的对象,并将其x属性的值改为6.show方法弹栈。 由于是两个引用指向同一个对象,不管是哪一个引用改变了引用的所指向的对象的中的值,其他引用再次使用都是改变后的值。* C.结论 * 对于基本类型形式参数改变不会影响到实际参数 * 对于引用类型形式参数改变会影响到实际参数
十四、private 关键字* A.private概述 * private可以修饰成员内容包括成员方法和成员变量 * 被private修饰的内容不能在其他类访问* B.使用步骤 * 1、通过private修饰属性 2、单对象是私有属性时需要用get和set来进行访问* C.完整代码 class Person { private int age; private String name; public void show() { System.out.println("age=" + age + ",name" + name); } }
十五、this 关键字来区分成员变量和局部变量同名的问题 A.什么时候用 * 当类中存在成员变量和局部变量同名的时候为了区分,就需要使用this关键字
谁调用的这个方法,这个this就指的谁。 this是本类方法的引用* B.代码 class Person { private int age; private String name; public void speak() { this.name = "小强"; this.age = 18; System.out.println("name=" + this.name + ",age=" + this.age); } } class PersonDemo { public static void main(String[] args) { Person p = new Person(); p.speak(); } }
十六、this内存图十七、继承特性
*A:继承的概念 *a:继承描述的是事物之间的所属关系,通过继承可以使多种事物之间形成一种关系体系 *b:在Java中,类的继承是指在一个现有类的基础上去构建一个新的类, 构建出来的新类被称作子类,现有类被称作父类 *B:继承关系的子类特点 *a:子类会自动拥有父类所有非private修饰的属性和方法 *A:继承的格式 class 子类 extends 父类 {} *B:雇员(Employee)与研发部员工(Developer)案例: *cn.itcast.demo01包下: *Employee.java: /* * 定义员工类Employee */ class Employee { String name; // 定义name属性 public void work() {// 定义员工的工作方法 System.out.println("尽心尽力地工作"); } } *Developer.java: /* * 定义研发部员工类Developer 继承 员工类Employee * 继承了父类中所有非private修饰的成员变量 */ class Developer extends Employee { // 定义一个打印name的方法 public void printName() { System.out.println("name=" + name); } } *测试员工类与研发部员工类: /* * 定义测试类 */ public class Example01 { public static void main(String[] args) { Developer d = new Developer(); // 创建一个研发部员工类对象 d.name = "小明"; // 为该员工类的name属性进行赋值 d.printName(); // 调用该员工的printName()方法 d.work(); // 调用Developer类继承来的work()方法 } } *通过子类对象既可以调用自身的非private修饰的成员,也可以调用父类的非private修饰的成员
十八、继承后子类父类成员变量的特点 *A:继承的概念 *a:继承描述的是事物之间的所属关系,通过继承可以使多种事物之间形成一种关系体系 *b:在Java中,类的继承是指在一个现有类的基础上去构建一个新的类, 构建出来的新类被称作子类,现有类被称作父类 *B:继承关系的子类特点 *a:子类会自动拥有父类所有非private修饰的属性和方法 *A:继承的格式 class 子类 extends 父类 {} *B:雇员(Employee)与研发部员工(Developer)案例: *cn.itcast.demo01包下: *Employee.java: /* * 定义员工类Employee */ class Employee { String name; // 定义name属性 public void work() {// 定义员工的工作方法 System.out.println("尽心尽力地工作"); } } *Developer.java: /* * 定义研发部员工类Developer 继承 员工类Employee * 继承了父类中所有非private修饰的成员变量 */ class Developer extends Employee { // 定义一个打印name的方法 public void printName() { System.out.println("name=" + name); } } *测试员工类与研发部员工类: /* * 定义测试类 */ public class Example01 { public static void main(String[] args) { Developer d = new Developer(); // 创建一个研发部员工类对象 d.name = "小明"; // 为该员工类的name属性进行赋值 d.printName(); // 调用该员工的printName()方法 d.work(); // 调用Developer类继承来的work()方法 } } *通过子类对象既可以调用自身的非private修饰的成员,也可以调用父类的非private修饰的成员
十九、继承后子类父类成员方法的特性——子类重写父类的方法A:继承后子类父类成员方法的特性 a:子类的对象调用方法的时候,子类自己有,使用子类,子类自己没有调用的父类 class Fu{ public void show(){ System.out.println("Fu类中的show方法执行"); } } class Zi extends Fu{ public void show2(){ System.out.println("Zi类中的show2方法执行"); } } public class Test{ public static void main(String[] args) { Zi z = new Zi(); z.show(); //子类中没有show方法,但是可以找到父类方法去执行 z.show2(); } } b:为什么要有重写? class Fu{ public void method(){ //上千行代码 //Fu类中的方法最先存在,那么如果项目需求变了,该方法 //功能不能够满足我们的需求,此时我们也不会去改这个方法 //因为项目中可能有大量的功能已经使用到该方法,如果随意修改可能使调用该方法的功能出现问题 //所以使用重写方式基于原有功能提供更强的功能 } } class Zi extends Fu{ } c:子类中出现与父类一模一样的方法时,会出现覆盖操作,也称为override重写、复写或者覆盖 class Fu{ public void show(){ System.out.println("Fu show"); } } class Zi extends Fu{ //子类复写了父类的show方法 public void show(){ System.out.println("Zi show"); } } public class Test{ public static void main(String[] args) { Zi z = new Zi(); z.show(); //Zi show 子类有直接使用子类 } }
二十、方法覆盖的案例 //手机类 class Phone{ public void sendMessage(){ System.out.println("发短信"); } public void call(){ System.out.println("打电话"); } public void showNum(){ System.out.println("来电显示号码"); } } //智能手机类 class NewPhone extends Phone{ //覆盖父类的来电显示号码功能,并增加自己的显示姓名和图片功能 //从现实生活角度考虑沿用原有的showNum方法名便于用户更快熟悉和接受,而不是再起个新的名字 //用户还需要花费大量时间慢慢接受 public void showNum(){ //调用父类已经存在的功能使用super //如果不加super这是调用子类自身的showNum(),自己调用自己,递归 //方法不断入栈导致内存溢出 super.showNum(); //增加自己特有显示姓名和图片功能 System.out.println("显示来电姓名"); System.out.println("显示头像"); } } public class Test { public static void main(String[] args) { new NewPhone().showNum();//来电显示 显示来电姓名 显示头像 } }
二十一、方法覆盖的注意事项A:方法覆盖的注意事项 a:权限:子类方法覆盖父类方法,必须要保证权限大于等于父类权限。 四大权限:public>默认=protected>private
class Fu{ void show(){ } public void method(){ } } class Zi() extends Fu{ public void show(){//编译运行没问题 } void method(){//编译错误 } } b:方法定义:子类方法和要重写的父类的方法:方法的方法名和参数列表都要一样。 关于方法的返回值: 如果是基本数据类型,子类的方法和重写的父类的方法返回值类型必须相同 如果是引用数据类型,子类的方法和重写的父类的方法返回值类型可以相同或者子类方法的返回值类型是父类方法返回值类型的子类 class Fu{ int show(){ } public Fu method(){ } public Fu method2(){ } } class Zi() extends Fu{ public int show(){//返回值为基本类型的重写 } public Fu method(){//子类的方法和重写的父类的方法返回值类型可以相同 } public Zi method2(){//子类方法的返回值类型是父类方法返回值类型的子类 } } c:重载与重写对比: 重载: 权限修饰符(public private 默认):无关 方法名:重载的两个方法的方法名必须相同 形参列表: 形参类型的顺序不同 形参的个数不同 形参的类型不同 三者至少满足一个 返回值类型: 重载与返回值类型无关 重写: 权限修饰符(public private 默认): 子类方法的权限>=父类的方法的权限 方法名: 子类方法和父类方法必须相同 形参列表: 子类方法和父类方法的形参列表必须相同 返回值类型: 基本类数据类型: 必须相同 引用数据类型: 子类方法的返回值类型和父类方法的返回值类型相同 或者 子类方法的返回值类型是父类方法的返回值类型的 子类
二十二、抽象 A:抽象类的产生 a:分析事物时,发现了共性内容,就出现向上抽取。会有这样一种特殊情况,就是方法功能声明相同,但方法功能主体不同。那么这时也可以抽取,但只抽取方法声明,不抽取方法主体。那么此方法就是一个抽象方法。 A:抽象方法定义的格式: a:public abstract 返回值类型 方法名(参数); 抽象类定义的格式: abstract class 类名 { } b:抽象类示例代码: /* * 定义类开发工程师类 * EE开发工程师 : 工作 * Android开发工程师 : 工作 * * 根据共性进行抽取,然后形成一个父类Develop * 定义方法,工作: 怎么工作,具体干什么呀 * * 抽象类,不能实例化对象, 不能new的 * 不能创建对象的原因: 如果真的让你new了, 对象.调用抽象方法,抽象方法没有主体,根本就不能运行 * 抽象类使用: 定义类继承抽象类,将抽象方法进行重写,创建子类的对象 */ public abstract class Develop { //定义方法工作方法,但是怎么工作,说不清楚了,讲不明白 //就不说, 方法没有主体的方法,必须使用关键字abstract修饰 //抽象的方法,必须存在于抽象的类中,类也必须用abstract修饰 public abstract void work(); }A:抽象类的使用方式 /* * 定义类,JavaEE的开发人员 * 继承抽象类Develop,重写抽象的方法 */public class JavaEE extends Develop{ //重写父类的抽象方法 //去掉abstract修饰符,加上方法主体 public void work(){ System.out.println("JavaEE工程师在开发B/S 软件"); }}/* * 定义Android类,继承开发人员类 * 重写抽象方法 */public class Android extends Develop{ public void work(){ System.out.println("Android工程师开发手机软件"); }}/* * 测试抽象类 * 创建他的子类的对象,使用子类的对象调用方法 */public class Test { public static void main(String[] args) { JavaEE ee = new JavaEE(); ee.work();//"JavaEE工程师在开发B/S 软件" Android and = new Android(); and.work();//"Android工程师开发手机软件" }}
二十三、抽象类的特点A:抽象类的特点 a:抽象类和抽象方法都需要被abstract修饰。抽象方法一定要定义在抽象类中。 b:抽象类不可以直接创建对象,原因:调用抽象方法没有意义。 c:只有覆盖了抽象类中所有的抽象方法后,其子类才可以创建对象。否则该子类还是一个抽象类。 之所以继承抽象类,更多的是在思想,是面对共性类型操作会更简单。 abstract class A{ public abstract void func(); public abstract void func2(); } class A2 extends A{//A2把A中的两个抽象方法都重写掉了 //A2类不再是抽象类 public void func(){} public void func2(){} }
abstract class A3 extends A{//含有抽象方法的类一定是抽象类 public void func(){ } //public abstract void func2();//func2相当于被继承下来 }
二十四、抽象关键字abstract不可以和那些关键字共存1:private:私有的方法子类是无法继承到的,也不存在覆盖, 而abstract和private一起使用修饰方法,abstract既要子类去实现这个方法, 而private修饰子类根本无法得到父类这个方法。互相矛盾。
2、:fina 3、static
二十五、接口
* A:接口的概念 接口是功能的集合,同样可看做是一种数据类型,是比抽象类更为抽象的”类”。 接口只描述所应该具备的方法,并没有具体实现,具体的实现由接口的实现类(相当于接口的子类)来完成。这样将功能的定义与实现分离,优化了程序设计。 请记住:一切事物均有功能,即一切事物均有接口。* A: 接口的定义 与定义类的class不同,接口定义时需要使用interface关键字。 定义接口所在的仍为.java文件,虽然声明时使用的为interface关键字的编译后仍然会产生.class文件。这点可以让我们将接口看做是一种只包含了功能声明的特殊类。* B : 定义格式 public interface 接口名 { 抽象方法1; 抽象方法2; 抽象方法3; }* C: 定义步骤 使用interface代替了原来的class,其他步骤与定义类相同: 接口中的方法均为公共访问的抽象方法 接口中无法定义普通的成员变量
二十六、接口的实现类* A: 类与接口的关系 类与接口的关系为实现关系,即类实现接口。实现的动作类似继承,只是关键字不同,实现使用implements。 其他类(实现类)实现接口后,就相当于声明:”我应该具备这个接口中的功能”。实现类仍然需要重写方法以实现具体的功能。* B: 类实现接口的格式 class 类 implements 接口 { 重写接口中方法 } * C:注意事项 在类实现接口后,该类就会将接口中的抽象方法继承过来,此时该类需要重写该抽象方法,完成具体的逻辑。 接口中定义功能,当需要具有该功能时,可以让类实现该接口,只声明了应该具备该方法,是功能的声明。 在具体实现类中重写方法,实现功能,是方法的具体实现。
二十七、接口中成员变量的特点* A:成员变量特点 * a 接口中可以定义变量,但是变量必须有固定的修饰符修饰,public static final 所以接口中的变量也称之为常量,其值不能改变。后面我们会讲解static与final关键字* B:案例 interface Demo { ///定义一个名称为Demo的接口。 public static final int NUM = 3;// NUM的值不能改变 }
二十八、接口中成员方法的特点* A:成员变量特点 * a 接口中可以定义变量,但是变量必须有固定的修饰符修饰,public static final 所以接口中的变量也称之为常量,其值不能改变。后面我们会讲解static与final关键字* B:案例 interface Demo { ///定义一个名称为Demo的接口。 public static final int NUM = 3;// NUM的值不能改变 }
二十九、类与接口的多实现* A:接口的多实现 了解了接口的特点后,那么想想为什么要定义接口,使用抽象类描述也没有问题,接口到底有啥用呢? 接口最重要的体现:解决多继承的弊端。将多继承这种机制在java中通过多实现完成了。* B 多实现的优点 * 怎么解决多继承的弊端呢? * 弊端:多继承时,当多个父类中有相同功能时,子类调用会产生不确定性。 * 其实核心原因就是在于多继承父类中功能有主体,而导致调用运行时,不确定运行哪个主体内容。 * 为什么多实现能解决了呢? * 因为接口中的功能都没有方法体,由子类来明确。* C :案例演示 interface Fu2{ void show2(); } class Zi implements Fu1,Fu2 { // 多实现。同时实现多个接口。 public void show1(){} public void show2(){} }
三十、类在继承类的同时实现多接口* A: 继承的同时实现接口 * 接口和类之间可以通过实现产生关系,同时也学习了类与类之间可以通过继承产生关系。当一个类已经继承了一个父类,它又需要扩展额外的功能,这时接口就派上用场了。 * 子类通过继承父类扩展功能,通过继承扩展的功能都是子类应该具备的基础功能。如果子类想要继续扩展其他类中的功能呢?这时通过实现接口来完成。 * 接口的出现避免了单继承的局限性。父类中定义的事物的基本功能。接口中定义的事物的扩展功能。* B: 代码演示 class Fu { public void show(){} } interface Inter { pulbic abstract void show1(); } class Zi extends Fu implements Inter { public void show1() { } } 接口的出现避免了单继承的局限性。父类中定义的事物的基本功能。接口中定义的事物的扩展功能。
三十一、接口多继承* A: 接口的多继承 * 学习类的时候,知道类与类之间可以通过继承产生关系,接口和类之间可以通过实现产生关系,那么接口与接口之间会有什么关系。 * 多个接口之间可以使用extends进行继承。* B 代码演示 interface Fu1{ void show(); } interface Fu2{ void show1(); } interface Fu3{ void show2(); } interface Zi extends Fu1,Fu2,Fu3{ void show3(); } 在开发中如果多个接口中存在相同方法,这时若有个类实现了这些接口,那么就要实现接口中的方法,由于接口中的方法是抽象方法,子类实现后也不会发生调用的不确定性。
三十二、接口和抽象类的区别* A: 明白了接口思想和接口的用法后,接口和抽象类的区别是什么呢?接口在生活体现也基本掌握,那在程序中接口是如何体现的呢? 通过实例进行分析和代码演示抽象类和接口的用法。* B: 举例: * 犬: 行为: 吼叫; 吃饭; * 缉毒犬: 行为: 吼叫; 吃饭; 缉毒;* C:思考: * 由于犬分为很多种类,他们吼叫和吃饭的方式不一样,在描述的时候不能具体化,也就是吼叫和吃饭的行为不能明确。 * 当描述行为时,行为的具体动作不能明确,这时,可以将这个行为写为抽象行为,那么这个类也就是抽象类。 * 可是当缉毒犬有其他额外功能时,而这个功能并不在这个事物的体系中。这时可以让缉毒犬具备犬科自身特点的同时也有其他额外功能,可以将这个额外功能定义接口中。* D: 代码演示 interface 缉毒{ public abstract void 缉毒(); } //定义犬科的这个提醒的共性功能 abstract class 犬科{ public abstract void 吃饭(); public abstract void 吼叫(); } // 缉毒犬属于犬科一种,让其继承犬科,获取的犬科的特性, //由于缉毒犬具有缉毒功能,那么它只要实现缉毒接口即可,这样即保证缉毒犬具备犬科的特性,也拥有了缉毒的功能 class 缉毒犬 extends 犬科 implements 缉毒{ public void 缉毒() { } void 吃饭() { } void 吼叫() { } } class 缉毒猪 implements 缉毒{ public void 缉毒() { } }* E: 接口和抽象类区别总结 相同点: 都位于继承的顶端,用于被其他类实现或继承; 都不能直接实例化对象; 都包含抽象方法,其子类都必须覆写这些抽象方法; 区别: 抽象类为部分方法提供实现,避免子类重复实现这些方法,提高代码重用性;接口只能包含抽象方法; 一个类只能继承一个直接父类(可能是抽象类),却可以实现多个接口;(接口弥补了Java的单继承) 抽象类是这个事物中应该具备的你内容, 继承体系是一种 is..a关系 接口是这个事物中的额外内容,继承体系是一种 like..a关系 二者的选用: 优先选用接口,尽量少用抽象类; 需要定义子类的行为,又要为子类提供共性功能时才选用抽象类;
三十三、多态* A: 多态概述 多态是继封装、继承之后,面向对象的第三大特性。 现实事物经常会体现出多种形态,如学生,学生是人的一种,则一个具体的同学张三既是学生也是人,即出现两种形态。 Java作为面向对象的语言,同样可以描述一个事物的多种形态。如Student类继承了Person类,一个Student的对象便既是Student,又是Person。 Java中多态的代码体现在一个子类对象(实现类对象)既可以给这个子类(实现类对象)引用变量赋值,又可以给这个子类(实现类对象)的父类(接口)变量赋值。 如Student类可以为Person类的子类。那么一个Student对象既可以赋值给一个Student类型的引用,也可以赋值给一个Person类型的引用。 最终多态体现为父类引用变量可以指向子类对象。 多态的前提是必须有子父类关系或者类实现接口关系,否则无法完成多态。 在使用多态后的父类引用变量调用方法时,会调用子类重写后的方法。* A:多态的定义格式: * 就是父类的引用变量指向子类对象 父类类型 变量名 = new 子类类型(); 变量名.方法名();* B: 普通类多态定义的格式 父类 变量名 = new 子类(); 举例: class Fu {} class Zi extends Fu {} //类的多态使用 Fu f = new Zi();* C: 抽象类多态定义格式 抽象类 变量名 = new 抽象类子类(); 举例: abstract class Fu { public abstract void method(); } class Zi extends Fu { public void method(){ System.out.println(“重写父类抽象方法”); } } //类的多态使用 Fu fu= new Zi();* D: 接口多态定义的格式 接口 变量名 = new 接口实现类(); 如: interface Fu { public abstract void method(); } class Zi implements Fu { public void method(){ System.out.println(“重写接口抽象方法”); } } //接口的多态使用 Fu fu = new Zi();* E: 注意事项 同一个父类的方法会被不同的子类重写。在调用方法时,调用的为各个子类重写后的方法。 如 Person p1 = new Student(); Person p2 = new Teacher(); p1.work(); //p1会调用Student类中重写的work方法 p2.work(); //p2会调用Teacher类中重写的work方法 当变量名指向不同的子类对象时,由于每个子类重写父类方法的内容不同,所以会调用不同的方法。
三十四、多态的向上向下造型* A: 多态的转型分为向上转型与向下转型两种:* B: 向上转型:当有子类对象赋值给一个父类引用时,便是向上转型,多态本身就是向上转型的过程。 使用格式: 父类类型 变量名 = new 子类类型(); 如:Person p = new Student();
* A: 向下转型:一个已经向上转型的子类对象可以使用强制类型转换的格式,将父类引用转为子类引用,这个过程是向下转型。如果是直接创建父类对象,是无法向下转型的! 使用格式: 子类类型 变量名 = (子类类型) 父类类型的变量; 如:Student stu = (Student) p; //变量p 实际上指向Student对象
三十五、instanceof关键字* A: 向下转型:一个已经向上转型的子类对象可以使用强制类型转换的格式,将父类引用转为子类引用,这个过程是向下转型。如果是直接创建父类对象,是无法向下转型的! 使用格式: 子类类型 变量名 = (子类类型) 父类类型的变量; 如:Student stu = (Student) p; //变量p 实际上指向Student对象
三十六、构造方法 * A:构造方法的引入 在开发中经常需要在创建对象的同时明确对象的属性值,比如员工入职公司就要明确他的姓名、年龄等属性信息。 那么,创建对象就要明确属性值,那怎么解决呢?也就是在创建对象的时候就要做的事情,当使用new关键字创建对象时,怎么给对象的属性初始化值呢? 这就要学习Java另外一门小技术,构造方法。* B: 那什么是构造方法呢? 从字面上理解即为构建创造时用的方法,即就是对象创建时要执行的方法。既然是对象创建时要执行的方法,那么只要在new对象时, 知道其执行的构造方法是什么,就可以在执行这个方法的时候给对象进行属性赋值。* A: 构造方法的作用: 在new的同时给成员变量赋值,给对象属性进行初始化。* B: 举例: Perons p = new Person("张三",23); 在new 的时候给p对象的name属性和age属性进行赋值,使这个对象的属性有值。 * A: 构造方法定义 构造方法的格式: 修饰符 构造方法名(参数列表) { }* B: 构造方法的体现: 构造方法没有返回值类型。也不需要写返回值。因为它是为构建对象的,对象创建完,方法就执行结束。 构造方法名称必须和类型保持一致。 构造方法没有具体的返回值。 构造方法的代码体现:* C: 构造方法举例 class Person { // Person的成员属性age和name private int age; private String name; // Person的构造方法,拥有参数列表 Person(int a, String nm) { // 接受到创建对象时传递进来的值,将值赋给成员属性 age = a; name = nm; } }* D: 构造方法运行特点: 在new 对象的时候自动调用执行。
三十七、构造方法的内存和调用赋值A:内存加载的过程 有一个Person类, 创建Person 对象new Person() 1、首先会将main方法压入栈中,执行main方法中的 new Person(23,"张三"); 2、在堆内存中分配一片区域,用来存放创建的Person对象,这片内存区域会有属于自己的内存地址(0x88)。然后给成员变量进行默认初始化(name=null,age=0)。 3、执行构造方法中的代码(age = a ; name = nm;),将变量a对应的23赋值给age,将变量nm对应的”张三赋值给name,这段代码执行结束后,成员变量age和name的值已经改变。执行结束之后构造方法弹栈,Person对象创建完成。将Person对象的内存地址0x88赋值给p2。* A: 理解构造方法的格式和基本功能之后,现在就要研究构造方法是怎么执行的呢?在创建对象的时候是如何初始化的呢? 构造方法是专门用来创建对象的,也就是在new对象时要调用构造方法。现在来看看如何调用构造方法。* B: 案例 class Person { // Person的成员属性age和name private int age; private String name; // Person的构造方法,拥有参数列表 Person(int a, String nm) { // 接受到创建对象时传递进来的值,将值赋给成员属性 age = a; name = nm; } public void speak() { System.out.println("name=" + name + ",age=" + age); } } class PersonDemo { public static void main(String[] args) { // 创建Person对象,并明确对象的年龄和姓名 Person p2 = new Person(23, "张三"); p2.speak(); } } 上述代码演示了创建对象时构造方法的调用。即在创建对象时,会调用与参数列表对应的构造方法
三十八、构造方法的重载A:内存加载的过程 有一个Person类, 创建Person 对象new Person() 1、首先会将main方法压入栈中,执行main方法中的 new Person(23,"张三"); 2、在堆内存中分配一片区域,用来存放创建的Person对象,这片内存区域会有属于自己的内存地址(0x88)。然后给成员变量进行默认初始化(name=null,age=0)。 3、执行构造方法中的代码(age = a ; name = nm;),将变量a对应的23赋值给age,将变量nm对应的”张三赋值给name,这段代码执行结束后,成员变量age和name的值已经改变。执行结束之后构造方法弹栈,Person对象创建完成。将Person对象的内存地址0x88赋值给p2。* A: 理解构造方法的格式和基本功能之后,现在就要研究构造方法是怎么执行的呢?在创建对象的时候是如何初始化的呢? 构造方法是专门用来创建对象的,也就是在new对象时要调用构造方法。现在来看看如何调用构造方法。* B: 案例 class Person { // Person的成员属性age和name private int age; private String name; // Person的构造方法,拥有参数列表 Person(int a, String nm) { // 接受到创建对象时传递进来的值,将值赋给成员属性 age = a; name = nm; } public void speak() { System.out.println("name=" + name + ",age=" + age); } } class PersonDemo { public static void main(String[] args) { // 创建Person对象,并明确对象的年龄和姓名 Person p2 = new Person(23, "张三"); p2.speak(); } } 上述代码演示了创建对象时构造方法的调用。即在创建对象时,会调用与参数列表对应的构造方法
三十九、构造方法和一般方法的区别* A: 目前为止,学习两种方法,分别为构造方法和一般方法,那么他们之间有什么异同呢? 1.格式不同 构造方法 : 修饰符 类名(参数类型 参数 ...){ 初始化成员变量 } 一般方法: 需要有返回值类型 2.作用不同 构造方法一般用来给成员变量初始化; 一般方法根据需求而定; 3.调用方式不同 构造方法创建对象时调用, 或者this() super() 语句调用 普通方法需要对象调用或者静态方法直接调用静态方法. 4.执行不同 构造方法在对象创建时就执行了,而且只执行一次。 一般方法是在对象创建后,需要使用时才被对象调用,并可以被多次调用。
四十、this在构造方法之间的调用* A: 在之前学习方法之间调用时,可以通过方法名进行调用。可是针对构造方法,无法通过构造方法名来相互调用。 构造方法之间的调用,可以通过this关键字来完成。 构造方法调用格式: this(参数列表);* B:调用构造方法的案例 class Person { // Person的成员属性 private int age; private String name; // 无参数的构造方法 Person() { } // 给姓名初始化的构造方法 Person(String nm) { name = nm; } // 给姓名和年龄初始化的构造方法 Person(String nm, int a) { // 由于已经存在给姓名进行初始化的构造方法 name = nm;因此只需要调用即可 // 调用其他构造方法,需要通过this关键字来调用 this(nm); // 给年龄初始化 age = a; } }* A: 被加载的代码 class Person { private int age; private String name; Person() { } Person(String nm) { name = nm; } Person(String nm, int a) { this(nm); age = a; } } class PersonDemo { public static void main(String[] args) { Person p = new Person("张三", 23); } }* B: 构造方法调用的原理图* 图略 1、先执行main方法,main方法压栈,执行其中的new Person(“张三”,23); 2、堆内存中开辟空间,并为其分配内存地址0x33,,紧接着成员变量默认初始化(name=null age = 0); 3、拥有两个参数的构造方法(Person(String nm , int a))压栈,在这个构造方法中有一个隐式的this,因为构造方法是给对象初始化的,那个对象调用到这个构造方法,this就指向堆中的那个对象。 4、由于Person(String nm , int a)构造方法中使用了this(nm);构造方法Person(String nm)就会压栈,并将“张三”传递给nm。在Person(String nm , int a)构造方法中同样也有隐式的this,this的值同样也为0x33,这时会执行其中name = nm,即把“张三”赋值给成员的name。当赋值结束后Person(String nm , int a)构造方法弹栈。 5、程序继续执行构造方法(Person(String nm , int a)中的age = a;这时会将23赋值给成员属性age。赋值结束构造方法(Person(String nm , int a)弹栈。 6、当构造方法(Person(String nm , int a)弹栈结束后,Person对象在内存中创建完成,并将0x33赋值给main方法中的p引用变量。 注意: this到底代表什么呢?this代表的是对象,具体代表哪个对象呢?哪个对象调用了this所在的方法,this就代表哪个对象。 调用其他构造方法的语句必须定义在构造方法的第一行,原因是初始化动作要最先执行。
四十一、this的简易应用* A: 当在方法中出现了局部变量和成员变量同名的时候,那么在方法中怎么区别局部变量成员变量呢?可以在成员变量名前面加上this.来区别成员变量和局部变量* B: 举例1 class Person { private int age; private String name; // 给姓名和年龄初始化的构造方法 Person(String name, int age) { // 当需要访问成员变量是,只需要在成员变量前面加上this.即可 this.name = name; this.age = age; } public void speak() { System.out.println("name=" + this.name + ",age=" + this.age); } } class PersonDemo { public static void main(String[] args) { Person p = new Person("张三", 23); p.speak(); } }* C: 举例2 学习完了构造方法、this的用法之后,现在做个小小的练习。 需求:在Person类中定义功能,判断两个人是否是同龄人 class Person { private int age; private String name; // 给姓名和年龄初始化的构造方法 Person(String name, int age) { // 当需要访问成员变量是,只需要在成员变量前面加上this.即可 this.name = name; this.age = age; } public void speak() { System.out.println("name=" + this.name + ",age=" + this.age); } // 判断是否为同龄人 public boolean equalsAge(Person p) { // 使用当前调用该equalsAge方法对象的age和传递进来p的age进行比较 // 由于无法确定具体是哪一个对象调用equalsAge方法,这里就可以使用this来代替 /* * if(this.age == p.age) { return true; } return false; */ return this.age = p.age; } }
四十二、super 关键字* A: 构造方法第一行,写this()还是super()* this() 是调用本类的构造方法,super()是调用父类的构造方法, 且两条语句不能同时存在* 保证子类的所有构造方法调用到父类的构造方法即可* B: 小结:* 无论如何,子类的所有构造方法,直接或间接必须调用到父类构造方法;* 子类的构造方法什么都不写,默认的构造方法第一行super()
* A: 构造方法第一行,写this()还是super()* this() 是调用本类的构造方法,super()是调用父类的构造方法, 且两条语句不能同时存在* 保证子类的所有构造方法调用到父类的构造方法即可* B: 小结:* 无论如何,子类的所有构造方法,直接或间接必须调用到父类构造方法;* 子类的构造方法什么都不写,默认的构造方法第一行super()
* A: 子父类中构造方法的调用 在创建子类对象时,父类的构造方法会先执行,因为子类中所有构造方法的第一行有默认的隐式super();语句。* B: 格式: 调用本类中的构造方法 this(实参列表); 调用父类中的空参数构造方法 super(); 调用父类中的有参数构造方法* A:子类构造方法,有一个默认添加的构造方法 public class Student extends Person { public Student(){ super(); } }* B :为什么子类对象创建都要访问父类中的构造方法?因为子类继承了父类的内容,所以创建对象时,必须要先看父类是如何对其内容进行初始化的,看如下程序 public class Test { public static void main(String[] args) { new Zi(); } } class Fu{ int num ; Fu(){ System.out.println("Fu构造方法"+num); num = 4; } } class Zi extends Fu{ Zi(){ //super(); 调用父类空参数构造方法 System.out.println("Zi构造方法"+num); } } 执行结果:
Fu构造方法0 Zi构造方法4
通过结果发现,子类构造方法执行时中,调用了父类构造方法,这说明,子类构造方法中有一句super()。 那么,子类中的构造方法为什么会有一句隐式的super()呢? 原因:子类会继承父类中的内容,所以子类在初始化时,必须先到父类中去执行父类的初始化动作。这样,才可以使用父类中的内容。 当父类中没有空参数构造方法时,子类的构造方法必须有显示的super语句,指定要访问的父类有参数构造方法。
四十三、总结及应用* A 创建子类对象过程的细节* 如果子类的构造方法第一行写了this调用了本类其他构造方法,那么super调用父类的语句还有吗?* 这时是没有的,因为this()或者super(),只能定义在构造方法的第一行,因为初始化动作要先执行。* 父类构造方法中是否有隐式的super呢?* 也是有的。记住:只要是构造方法默认第一行都是super();* 父类的父类是谁呢?super调用的到底是谁的构造方法呢?* Java体系在设计,定义了一个所有对象的父类Object* 注意: 类中的构造方法默认第一行都有隐式的super()语句,在访问父类中的空参数构造方法。所以父类的构造方法既可以给自己的对象初始化,也可以给自己的子类对象初始化。 如果默认的隐式super()语句在父类中没有对应的构造方法,那么必须在构造方法中通过this或者super的形式明确要调用的构造方法。* A: 练习:描述学生和工人这两个类,将他们的共性name和age抽取出来存放在父类中,并提供相应的get和set方法,同时需要在创建学生和工人对象就必须明确姓名和年龄 * 案例: //定义Person类,将Student和Worker共性抽取出来 class Person { private String name; private int age; public Person(String name, int age) { // super(); this.name = 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; } } class Student extends Person { // Student类的构造方法 Student(String name, int age) { // 使用super关键字调用父类构造方法,进行相应的初始化动作 super(name, age); } public void study() {// Studnet中特有的方法 System.out.println(this.getName() + "同学在学习"); } } class Worker extends Person { Worker(String name, int age) { // 使用super关键字调用父类构造方法,进行相应的初始化动作 super(name, age); } public void work() {// Worker 中特有的方法 System.out.println(this.getName() + "工人在工作"); } } public class Test { public static void main(String[] args) { Student stu = new Student("小明",23); stu.study(); Worker w = new Worker("小李",45); w.work(); } }
四十四、final 关键字* A: 概述 继承的出现提高了代码的复用性,并方便开发。但随之也有问题,有些类在描述完之后,不想被继承, 或者有些类中的部分方法功能是固定的,不想让子类重写。可是当子类继承了这些特殊类之后, 就可以对其中的方法进行重写,那怎么解决呢? 要解决上述的这些问题,需要使用到一个关键字final,final的意思为最终,不可变。 final是个修饰符,它可以用来修饰类,类的成员,以及局部变量。* A: final 修饰类 final修饰类不可以被继承,但是可以继承其他类。* B: 案例 class Yy {} final class Fu extends Yy{} //可以继承Yy类 class Zi extends Fu{} //不能继承Fu类
四十五、static关键字* A:概念 当在定义类的时候,类中都会有相应的属性和方法。而属性和方法都是通过创建本类对象调用的。 当在调用对象的某个方法时,这个方法没有访问到对象的特有数据时,方法创建这个对象有些多余。 可是不创建对象,方法又调用不了,这时就会想,那么我们能不能不创建对象,就可以调用方法呢? 可以的,我们可以通过static关键字来实现。static它是静态修饰符,一般用来修饰类中的成员。 使用:static修饰的类 为静态类 直接类名.方法 访问* A:特点1: 被static修饰的成员变量属于类,不属于这个类的某个对象。 (也就是说,多个对象在访问或修改static修饰的成员变量时,其中一个对象将static成员变量值进行了修改, 其他对象中的static成员变量值跟着改变,即多个对象共享同一个static成员变量)* B: 代码演示 class Demo { public static int num = 100; } class Test { public static void main(String[] args) { Demo d1 = new Demo(); Demo d2 = new Demo(); d1.num = 200; System.out.println(d1.num); //结果为200 System.out.println(d2.num); //结果为200 } }
四十六、匿名对象* A:匿名对象的概述 * 匿名对象是指创建对象时,只有创建对象的语句,却没有把对象地址值赋值给某个变量。* B:案例 public class Person{ public void eat(){ System.out.println(); } } 创建一个普通对象 Person p = new Person(); 创建一个匿名对象 new Person();* C: 匿名对象的特点 a:创建匿名对象直接使用,没有变量名。 new Person().eat() //eat方法被一个没有名字的Person对象调用了。 b:匿名对象在没有指定其引用变量时,只能使用一次。 new Person().eat(); 创建一个匿名对象,调用eat方法 new Person().eat(); 想再次调用eat方法,重新创建了一个匿名对象 c:匿名对象可以作为方法接收的参数、方法返回值使用 class Demo { public static Person getPerson(){ //普通方式 //Person p = new Person(); //return p; //匿名对象作为方法返回值 return new Person(); } public static void method(Person p){} } class Test { public static void main(String[] args) { //调用getPerson方法,得到一个Person对象 Person person = Demo.getPerson(); //调用method方法 Demo.method(person); //匿名对象作为方法接收的参数 Demo.method(new Person()); } }
四十七、内部类* A: 内部类的概述 将类写在其他类的内部,可以写在其他类的成员位置和局部位置,这时写在其他类内部的类就称为内部类。 其他类也称为外部类。* B: 什么时候使用内部类 在描述事物时,若一个事物内部还包含其他可能包含的事物,比如在描述汽车时,汽车中还包含这发动机, 这时发动机就可以使用内部类来描述。 class 汽车 { //外部类 class 发动机 { //内部类 } }* C: 内部类的分类 内部类分为成员内部类与局部内部类。 我们定义内部类时,就是一个正常定义类的过程,同样包含各种修饰符、继承与实现关系等。 在内部类中可以直接访问外部类的所有成员。
* A: 格式 成员内部类,定义在外部类中的成员位置。与类中的成员变量相似,可通过外部类对象进行访问* B: 定义格式 class 外部类 { 修饰符 class 内部类 { //其他代码 } }* C: 访问方式 外部类名.内部类名 变量名 = new 外部类名().new 内部类名();* D: 成员内部类代码演示 class Body {//外部类,身体 private boolean life= true; //生命状态 public class Heart { //内部类,心脏 public void jump() { System.out.println("心脏噗通噗通的跳") System.out.println("生命状态" + life); //访问外部类成员变量 } } } 访问内部类 public static void main(String[] args) { //创建内部类对象 Body.Heart bh = new Body().new Heart(); //调用内部类中的方法 bh.jump(); }
* A: 代码实现 public class Outer { int i = 1; class Inner { int i = 2; public void inner(){ int i = 3; System.out.println(Outer.this.i); } } }
* A 局部内部类,定义在外部类方法中的局部位置。与访问方法中的局部变量相似,可通过调用方法进行访问.* B 定义格式 class 外部类 { 修饰符 返回值类型 方法名(参数) { class 内部类 { //其他代码 } } }* C 访问方式 在外部类方法中,创建内部类对象,进行访问* D 局部内部类代码演示 定义类 class Party {//外部类,聚会 public void puffBall(){// 吹气球方法 class Ball {// 内部类,气球 public void puff(){ System.out.println("气球膨胀了"); } } //创建内部类对象,调用puff方法 new Ball().puff(); } } 访问内部类 public static void main(String[] args) { //创建外部类对象 Party p = new Party(); //调用外部类中的puffBall方法 p.puffBall(); }
* A: 概述 内部类是为了应对更为复杂的类间关系。查看源代码中会涉及到,而在日常业务中很难遇到,这里不做赘述。 最常用到的内部类就是匿名内部类,它是局部内部类的一种。 定义的匿名内部类有两个含义: 临时定义某一指定类型的子类 定义后即刻创建刚刚定义的这个子类的对象* B: 本质 匿名内部类的本质是一个实现了接口或继承了某个类的子类匿名对象.* C: 案例public interface Smoking { public abstract void smoking(); } /* * 实现类,实现接口 重写接口抽象方法,创建实现类对象 * class XXX implements Smoking{ * public void smoking(){ * * } * } * XXX x = new XXX(); * x.smoking(); * Smoking s = new XXX(); * s.smoking(); * * 匿名内部类,简化问题: 定义实现类,重写方法,建立实现类对象,合为一步完成 */测试类:public class Test { public static void main(String[] args) { //使用匿名内部类 /* * 定义实现类,重写方法,创建实现类对象,一步搞定 * 格式: * new 接口或者父类(){ * 重写抽象方法 * }; * 从 new开始,到分号结束 * 创建了接口的实现类的对象 */ new Smoking(){ public void smoking(){ System.out.println("人在吸烟"); } }.smoking(); }}
四十八、代码块 * A: 概述: 程序中用大括号括起来的代码叫代码块 * B: 分类 局部代码块 构造代码块 静态代码块 同步代码块 * C 局部代码块: 局部代码块是定义在方法或语句中 特点: 以”{}”划定的代码区域,此时只需要关注作用域的不同即可 方法和类都是以代码块的方式划定边界的 class Demo{ public static void main(String[] args) { { int x = 1; System.out.println("普通代码块" + x); } int x = 99; System.out.println("代码块之外" + x); } } 结果: 普通代码块1 代码块之外99 局部代码块作用:可以限定变量的声明周期.* D: 构造代码块 构造代码块是定义在类中成员位置的代码块 特点: 优先于构造方法执行,构造代码块用于执行所有对象均需要的初始化动作 每创建一个对象均会执行一次构造代码块。 public class Person { private String name; private int age; //构造代码块 { System.out.println("构造代码块执行了"); } Person(){ System.out.println("Person无参数的构造函数执行"); } Person(int age){ this.age = age; System.out.println("Person(age)参数的构造函数执行"); } } class PersonDemo{ public static void main(String[] args) { Person p = new Person(); Person p1 = new Person(23); } }* E: 静态代码块 静态代码块是定义在成员位置,使用static修饰的代码块。 特点: 它优先于主方法执行、优先于构造代码块执行,当以任意形式第一次使用到该类时执行。 该类不管创建多少对象,静态代码块只执行一次。 可用于给静态变量赋值,用来给类进行初始化。 public class Person { private String name; private int age; //静态代码块 static{ System.out.println("静态代码块执行了"); } }* F: 同步代码块(多线程学习)
四十九、修饰符的使用 * A: 概述: 程序中用大括号括起来的代码叫代码块 * B: 分类 局部代码块 构造代码块 静态代码块 同步代码块 * C 局部代码块: 局部代码块是定义在方法或语句中 特点: 以”{}”划定的代码区域,此时只需要关注作用域的不同即可 方法和类都是以代码块的方式划定边界的 class Demo{ public static void main(String[] args) { { int x = 1; System.out.println("普通代码块" + x); } int x = 99; System.out.println("代码块之外" + x); } } 结果: 普通代码块1 代码块之外99 局部代码块作用:可以限定变量的声明周期.* D: 构造代码块 构造代码块是定义在类中成员位置的代码块 特点: 优先于构造方法执行,构造代码块用于执行所有对象均需要的初始化动作 每创建一个对象均会执行一次构造代码块。 public class Person { private String name; private int age; //构造代码块 { System.out.println("构造代码块执行了"); } Person(){ System.out.println("Person无参数的构造函数执行"); } Person(int age){ this.age = age; System.out.println("Person(age)参数的构造函数执行"); } } class PersonDemo{ public static void main(String[] args) { Person p = new Person(); Person p1 = new Person(23); } }* E: 静态代码块 静态代码块是定义在成员位置,使用static修饰的代码块。 特点: 它优先于主方法执行、优先于构造代码块执行,当以任意形式第一次使用到该类时执行。 该类不管创建多少对象,静态代码块只执行一次。 可用于给静态变量赋值,用来给类进行初始化。 public class Person { private String name; private int age; //静态代码块 static{ System.out.println("静态代码块执行了"); } }* F: 同步代码块(多线程学习)
1
- (二十五)基础系列 面向对象
- (二十五)基础系列 入门
- (二十五)基础系列 多线程
- (二十五)基础系列 数据库
- C#面向对象 基础概念二十五个 (很基础,必须掌握的知识)
- C#面向对象 必须掌握的二十五个基础概念
- (二十五)基础系列 API和集合
- (二十五)基础系列 io流
- (二十五)基础系列 socket 与xml
- (二十五)基础系列 JSP/JSTL/EL
- (二十五)基础系列 AJAX、JQuery
- C++面向对象基础二
- 面向对象基础<二>---继承
- Java面向对象基础二
- 面向对象基础加强二
- 面向对象基础(二)
- Java面向对象基础(二)
- 面向对象基础(二)
- 刷题记录
- PLSQL编程3:plsql流程控制和循环结构
- 页面展示数据库信息不报错,后台存取页面信息报错。解决办法
- c语言作业(三)
- 线性筛法
- (二十五)基础系列 面向对象
- python @wraps修饰器
- linux zip/unzip命令详解
- lower_bound & binary_search & equal_range
- 中国剩余定理求解同余线性方程组(模数互素和非互素的情况)
- sizeof在程序中的深层剖析
- 预算聚合引擎模型分析
- Javascript模块化编程(三):require.js的用法
- tomcat安装配置