Java面向对象编程
jie
2020-10-10
在栈中开了一个内存空间, 还没有指向 在堆中开辟一个内存空间, 该对象在栈中有了地址 public ,abstract 写与不写都一样 主程序→ Java面向对象编程 1.类与对象 2.深入分析类与对象 4.this关键字 6.static关键字 7.代码块 8.面向对象案例分析 9.数组的定义与使用 10.引用传递实际应用 11.数据表与简单Java类映射 12.String类特点分析 13.String类常用方法 14.继承的定义与使用 15.覆写(重写) 16.综合案例:继承分析 17.Annotation注解 18.多态性 19.Object类 20.抽象类的定义与使用 21.包装类 22.接口的定义与使用 23.综合案例:抽象类与接口 24.泛型 25.包的定义及使用 26.UML图形 27.单例设计模式 28.枚举 29.异常的捕获及处理 30.内部类 31.函数式编程 32.链表的定义与使用 33.Eclipse开发工具 1.面向对象简介 3.类与对象的定义及使用 4.对象内存简单分析 5.对象引用分析 6.引用与垃圾产生分析 2.类与对象简介 Java最大的特点是面向对象的编程设计 核心本质 : 在整个面向对象的设计里面考虑的是一份标准的模块化设计, 使用时根据 标准进行拼装 三个主要特征 封装性 继承性 多态性 内部的操作对外不可见, 当内部的操作都不可以直接使用时才是安全的 在已有结构的基础上继续进行功能扩充, 是在继承的基础上扩充而来的概念, 指的是类型的转换处理 面向对象程序开发一般三个步骤 : OOA (Object-Oriented Analysis): 面向对象分析 OOD (Object-Oriented Design): 面向对象设计 OOP (Object Oriented Programming): 面向对象编程 类是对某一类事物的共性的抽象概念, 而对象描述的是一个具体的产物, 是一个类的 实例, 有方法(行为)和属性(状态) 类是一个模板, 而对象才是类可以使用的实例, 先有类再有对象 类; 一般有两个组成 成员属性(Field) 操作方法(Method) Java中类是一个独立的结构体 要用对象调用类, 声明对象 实例化对象 对象名称 = new 类名称(); 调用 调用类中的属性 调用类中的方法 对象名称.属性名称 对象名称.方法名称() 类名称 对象名称; 类名称 对象名称 = new 类名称(); Java中类属于引用数据类型, 困难之处在于内存管理, 操作中也会发生内存关系变化 堆内存: 栈内存 通过地址找到堆内存, 再找到对象内容 程序里堆内存空间的开辟是通过new()完成的 栈内存 per(0x00001) 堆内存 Animal() Peson() name = "张三" age = 18 Person per = new Person() ; Animal an = new Animal() ; per.name = "张三" ; per.age = 18 ; 保存的是对象的具体信息 保存的是一堆内存地址 an(0x00002) ....... ....... 错误 ......java.lang. NULLPointerException ...... JavaDemo.java:[行数] NULLPointer即指向异常, 没有开辟堆内存产生的问题(没有为对象实例化, 无法调 用), 只有引用数据类型存在此错误 内存引用传递的本质: 同一块内存空间可以被不同的栈内存指向 Person per = new Person ; person per2 = per ; 可以利用方法实现引用传递处理 Person类的实例化对象(内存地址, 数值)per可传递到方法中, 即可做形参 方法执行完毕后, 形参temp会断开连接 引用传递发生在方法上, 要注意方法的参数类型, 执行过程 引用传递使用不当会造成垃圾产生 Person per1 = new Person() ; Person per2 = new Person() ; 这会在堆开辟两块内存空间 per1 = per2 ; 这时per2指向改变, 但原来指向的堆空间还存在, 成为垃圾 垃圾空间 指没有任何栈内存指向的对内存空间 垃圾都将被GC(Garbage Collection)回收并释放内存空间, 该过程需要不少时间, 程 序开发中要尽量减少GC触发 成员属性封装 1.用private定义属性(对外部不可见) 3.设置属性的方法 4.获取属性的方法 public void setName(String n) ; public String getName() ; 一般方法都是对外提供的,不做封装处理, 而属性需要较高的安全性, 往往采用封装性 对其保护 没有封装处理外部可以修改属性, 可能会导致属性是错误的数据 封装方式 2.java封装setter开发标准 :setXxxxx(), getXxxx() 5.例 : 开发中, 类中的所有属性都必须用private封装(98%), 这是标准做法 该方法里可以对属性值进行限制 构造方法与匿名对象 通过构造方法实现实例化对象中的属性初始化处理, 只有在关键字new时使用构造方 构造方法定义要求 构造方法名称必须与类名称保持一致 构造方法不允许设置任何返回值类型 构造方法在使用关键字new'实例化对象时自动调用 定义构造方法 一个类至少存在一个构造方法, 构造方法相关疑问 为什么构造方法不能用void 编译器根据代码结构编译处理, 如果构造方法用void则如普通方法相同 构造方法与普通方法区别 构造方法是在类对象实例化时调用的, 普通方法是在类对象实例化产生之后调用的 构造方法重载 方法重载建议根据参数个数升序排列 匿名对象 new Person("张三", 10).方法名称() ; 对方法进行了调用, 但没有任何引用 this关键字 this调用本方法 三类描述 当前类中属性: this.属性 当前类中方法: this.方法() 当前对象 建议开发时, 只要是访问本类中属性,就加this 构造方法调用 this() 普通方法调用 this.方法() 构造方法重载里 , 可以用this([参数名称], .....) 调用本类中其他参数个数不同的方法, (构造方法的互相调用), 以减少代码重复 目的: 减少代码重复 this()语句只允许放在构造方法首行 构造方法互相调用时注意避免死循环 5.类的核心开发结构 类名称一定要有意义, 可以明确描述某一类事物 类中所有属性都必须使用private封装, 封装后必须提供setter(), getter()方法 类中可以有很多个构造方法, 且必须保留有无参构造方法 类中不允许出现输出语句, 所有输出内容的获取必须返回 [非必须]可以提供一个获取对象信息的方法, 暂时将此方法定义为getInfo() 训练: 建议写一个简单Java类, 囊括以上构造方法, 方法重载, this关键字等全部知识 将属性修改为公共属性, 存放在全局数据区 static属性可以由类名称直接调用 static虽定义在类中, 但不受到实例化对象影响, 不用实例化也可调用 首选非static, 考虑到公共信息存储时才使用static属性 声明static属性 声明static方法 限制: static方法里只能调用static属性, 方法; 要调用普通方法需要先实例化该普通 方法 static应用 普通代码块 构造代码块 静态代码块 同步代码块 多线程里的 定义在一个方法中的代码块, 即用{ } 括起来 作用: 防止相同变量名称带来的影响( 一个方法里不同的代码块可定义相同变量名称, 即全局, 局部变量) 特点: 实例化时执行, 构造块会先执行于构造方法 在类中{ }括起来的代码块 在类中用 static { } 定义的代码块 特点: 先执行于构造块, 无论实例化几次, 静态块都只执行一次 主要功能: 在类中为static属性初始化 注意: 静态代码块优先于主方法先执行 数组的基本定义 数组引用传递分析 foreach输出 数组动态初始化 数组静态初始化 数组类型 数组名称 [] = new 数据类型 [长度] ; 数组类型 [] 数组名称 = new 数据类型 [长度] ; 简化格式 数组类型 数组名称 [] ={ 数据1, 数据2, 数据3, ...} ; 完整格式 数据类型 数组名称 [] = new 数据类型 [] { 数据1, 数据2, 数据3, ...} ; error ArrayIndexOutOfBoundsException 数组越界异常(脚标超出范围) 常用 数组名称.length 获取数组长度 常用for循环控制数组 建议用完整格式定义 初始值为该数据类型对应的初始值 数组本身时引用数据类型, 可发生引用传递, 即一个堆内存被多个栈内存所指向 NullPointerException 空指向错误, 没有实例化new开辟堆内存空间 自动获取数组中每一个元素, 避免下标访问 语法 for (int temp : data) { } ; 二维数组 数组与方法 数组动态初始化 数组静态初始化 数组类型 数组名称 [][] = new 数据类型 [行数][列数] ; 数据类型 数组名称 [][] = new 数据类型 [][] { {数据, 数据, ...}, {数据, 数据, ...}, ...} ; 二维数组是数组的嵌套, 数组名称.length是行数, 数组名称[x].length是列数 public static int[] array() { int arr[] = new int[] {3, 4, 5, 6} : return arr ; // arr是地址 } 案例分析 数组冒泡排序 数组转置 数组相关类库 java.util.Arrays.sort(数组名称) 数组升序排序 System.arraycopy(源数组, 源数组开始点, 目标数组, 目标数组开始点, 拷贝长度) 数组拷贝 方法可变参数 pubilc static int sum(int ... data){ for(int temp : data) { } } 本质还是数组 对象数组 Java程序本身的各种数据类型都可以成为数组类型, 类也可以成为数组类型, 称为对 象数组 动态初始化 类 对象数组名称[] = new 类 [长度], 每一个元素的内容都是null 静态初始化 类 对象数组名称[] = new 类 [] {实例化对象, 实例化对象...} ; 所有开发都离不开对象数组 缺陷: 长度是固定的, 一旦确定不可更改 好处: 数据线性保存, 根据索引范围, 速度较快, 时间复杂度为"1" 存在堆内存中的对象是栈内存里的对象的名称引用?? 类关联结构 自身关联 合成设计模式 例: 例 : 都是类中常用的逻辑关系 人类的产品都是可以拆分,再重新组合的 数据表与简单的Java类映射转换 一对多映射 多对多映射 复杂多对多映射 String类简介 字符串比较 字符串常量描述的是String类的匿名对象 String类对象两种实例化方式比较 String对象常量池 字符串修改分析 主方法组成分析 JavaDoc文档简介 字符串与字符 字符串比较 字符串查找 字符串替换 字符串拆分 字符串截取 字符串格式化 其他操作方法 继承问题引出 继承的实现 子类对象实例化流程 继承定义限制 简单Java类定义往往根据数据表的结构来实现的 基本映射关系 数据实体表设计 = 类的定义 表中的字段 = 类的成员属性 表的一行记录 = 类的一行实例化对象 表的多行记录 = 对象数组 表的外键关联 = 引用关联 映射解决步骤 写出类的基本组成 通过引用配置关联字段关系 例: 图书 → 不同书籍 例: 用户 → 商品 → 商品属性 例: 用户授权管理 用户 → 所有角色 → 所有权限信息 → 所有用户信息 字符串不是一种基本数据类型, 是一个final类 String这个类里定义的字符串 每一个字符是保存在一个数组里 JDK1.9后 String类的数组用的是bype类型(字节), JDK1.8以前用的是字符数组 不能用"=="准确判断 要用equals()比较 "=="是数值比较, 如果用于对象比较, 比较的是两个内存的地址值 是Object类提供的比较方法, 可以直接进行字符串内容判断 strA.equals(strB) ; //返回布尔值 注意: strA不能是null, strB可以 所以要把字符串常量写在前面 String str = "mind" ; //直接赋值 String str = new String("mind") ; //构造方法实例化 即strA, strB两个对象指向的堆内存是同一个(数据共享), 对象重用 String strA = "mind" ; String strB = "mind" ; System.out.println(strA==strB); //地址[按段, 输出True 堆内存会提供一个"String对象池", 定义strB时首先会查找池 在堆内存中开辟新的内存空间, 不会自动保存到字符串池 也可以实现手动入池 : String str = new String("mind").intern() ; 一般建议用直接赋值 目的 : 实现数据的共享处理 分两种 静态常量池 运行时常量池 在 *.class加载时自动将程序里保存好的字符串, 普通常量, 或者类, 普通方法等等. 全 部进行分配 *.class加载后, 里面可能有些变量, 这时提供的常量池 字符串内容不可修改 字符串"+="拼接时只是生成新的内容空间, 并且对象改变指向, 原本的字符串成为垃 开发中String类不要进行频繁修改, 会产生很多垃圾 String str = "a" + "b" + "c" ; 这会保存成一个静态值, 不是频繁修改, 不会产生垃圾 for(int x = 0; x<1000; x++){ str += x ; } 此程序将会产生1000多个垃圾空间 public static void main(String args[]) ; public 描述的是一种访问权限, 主方法时一切的开始点, 一定是公共的 static 程序执行通过类名称完成, 所以此方法是由类直接调用 void 主方法是一切的开始, 一开始就没有返回的可能了 main String args[] 是一个系统定义好的方法名称 字符串数组, 可以实现程序启动参数的接收 程序执行时可以设置参数, 每个参数以空格分隔, 如参数本身有空格, 必须使用""包装 例 : java StringDemo first second public class StringDemo{ public static void main(String args[]){ for(String arg : args){ System.out.println(arg) ; } } } 可以暂时通过这种启动参数实现数据的输入模拟 开发中要大量使用Java的API文档 JDk1.9之前, Java中常用类库会在JVM启动时全部加载, 性能会有所下降 1.9后模块(Module)化设计, 模块中包含大量程序开发包, 如 要打开String类的相关定义, 可以打开java.lang这个包, 里面有个String类 类页面 类的完整定义 Class String 类相关说明信息 成员属性摘要(Field Summary) 构造方法摘要(Constructor Summary) 看见有"Deprecated"描述的方法不建议使用 方法摘要(Method Summary) 对方法和成员的详细说明, 有返回值, 方法名称和相应的参数 注意 别用自动翻译的文档, 要看英文的 str.charAt(index) 获取指定索引位置的字符, 下标是从0开始的 str.toCharArray() 将字符串变为字符数组 new String(数组名称), 可将字符数组变为String new String(数组名称, 0, 5), 从下标0开始取5个变为String 字符串与字节 字符串与字节转换, 主要目的是为了进行 二进制的数据传输 或者 编码转换 getBytes() 字符串变为字节数组 equals(String anObject) 区分大小写的相等判断 equalsIgnoreCase(String anotherString) 不区分大小写的相等判断 compareTo(String anotherString) 字符串大小比较, 返回 int类型 返回值: 大于(>0), 小于(<0), 等于(=0) compareToIgnoreCase(String anotherString) 不区分大小写的字符串大小比较 public boolean contains(String str) 判断字符串是否存在(1.5之后才有的) public int indexOf(String str) 从头查找字符串位置, 找不到返回 -1 indexOf() public int indexOf(String str, int fromIndex) 从指定位置开始查找字符串位置 public int lastIndexOf(String str) 由后向前查找指定字符串位置 public int lastIndexOf(String str, int fromIndex) 从指定位置由后向前查找指定字符串位置 public boolean startsWith(String prefix) 判断是否以指定字符串开头 public boolean startsWith(String prefix, int toffset) 由指定位置判断是否以指定字符串开头 public boolean endsWith(String suffix) 判断是否以指定字符串结尾 字符串位置的查询, 在一些开发中可以利用此进行索引的确定 public String replaceAll(String regex, String replacement) 全部替换, regex(正则表达式) public String replaceFirst(String regex, String replacement) 替换首个 public String[] split(String regex) 按照指定字符串全部拆分 public String[] split(String regex, int limit) 按照指定字符串拆分指定个数, 后面不拆了 拆分时遇见拆不了的情况(涉及正则表达式(regex)的问题), 最简单的理解是使用"\\"进行转义 public String subString(int beginIndex) 从指定位置截取到结尾 public String subString(int beginIndex, int endIndex) 截取指定索引范围的字符串 subString() 开发中开始或结束索引往往是通过indexOf()来的 JDK1.5开始提供, 类似c语言的格式化输出语句, 利用占位符输出, 如字符串(%s), 整数(%d)等等 public static String format(String format, 各种类型 ... args) 根据指定结构进行文本格式化显示 (有static即可以直接调用, String fomat()) 例 : public boolean isEmpty() ""和null, 一个表示有实例化对象, 一个表示没有实例化对象 判断字符串的内容, 一定要在有实例化对象的时候进行调用 判断是否为空字符串(是"", 不是null) public String concat(String str) 字符串连接,跟"+"的区别是这个定义的字符串没有入池(好像没什么用处) public int length() 计算字符串长度 public String intern() 让字符串入池 public String trim() 去除字符串左右的空格信息(有空格时字符串查找可能会出现错误) public String toUpperCase() public String toLowerCase() 转大写 转小写 自定义实现首字母大写的方法(官方没提供) 扩充已有类的功能, 解决代码重复问题 依靠extends关键字 class 子类 extends 父类 {} 有时会把子类称为派生类, 父类称为超类(SuperClass) 子类实例化时一定要先实例化父类 super()表示子类构造调用父类构造的语句, 只允许放在子类构造方法首行, super()默认调用父类无参构造,但若父类没有无参构造, 要用super()明确调用有参构 super() 即使没有实例化父类, 系统也会自动实例化父类 (注意会调用父类的构造方法) 目的是为了实现所有的属性 空间的分配 super()与this (this是调用本类构造) 都必须在构造方法首行, 所以两个语句不允许 同时出现 方法覆写 方法覆写限制 属性覆盖 final关键字 Java中不允许多重继承, 只允许多层继承 class A{} class B{} class C extends A, B{} 多重继承 多层继承 多重继承时希望同时继承多个类的方法 多层继承是希望扩展已有类的功能 class A{} class B extends A{} class C extends B{} 理论上层次不应该超过三层 子类不能直接访问父类的private属性(隐式继承) 子类定义了与父类方法一样的方法 覆写是为了优化父类的功能 子类中调用父类方法, 方法前一定要追加"super." 权限 : public > default(不写) > private 子类覆写方法权限不能低于父类 父类的private方法不能被覆写, 子类如果定义了同样的方法, 则这是一个新的方法 建议方法都用public 子类定义了与父类相同名称的属性 描述的是终接器的概念, 定义不能被继承的类, 不能被覆写的方法, 常量 final class Person{} 该类不能被继承 public final void connet() {} 不能被覆写的类 可用final定义常量 final String str = "mind" ; //常量 public static final int ON =1 ; //全局常量, 每个字母必须大写表示 Annotation简介 准确覆写 过期声明 压制警告 程序 维护时的一些常量可能需要更改 最初时开发人员修改源代码, 后来引入配置文件(配置文件暴多), 到现在的注解(又把 配置文件放回代码中, 并让其与代码分离) 如果全部使用注解, 难度太高, 所以 现在的开发基本使用配置文件 + 注解 几个基本注解 @Overrde @Deprecated @SuppressWarnings 明确表示该方法是一个覆写方法, 写在方法上方(不是方法里), 旨在防止由于疏忽 方法没有覆写成功, 但是程序认为这是一个新方法, 没有报错, 是个结构性的注解 表示这是一个旧的方法, 不要用 用于代码升级过渡, 因为旧的方法不能马上全部换掉 不显示,提示, 警告 (如果你已经明确指导错误在哪里) 例 :@SuppressWarnings({"deprecation"}) 多态性简介 对象向上转型 对象向下转型 instanceof关键字 在继承的基础上扩展出来的, 可以实现父子类之间的互相转换处理 两种实现模式 方法的多态性 对象的多态性 方法的重载, 重载就是一种多态性 方法的覆写 父类 父类实例 = 子类实例() 自动完成转换, (大多数情况) 子类 子类实例 = (子类)父类实例() 强制转换,(很少用) 作用 : 让父类可以调用子类覆写的方法, 注意不能调用子类特有的方法 实现参数的统一 要转型的对象原本就是子类对象通过向上转型得到的, 否则 会" ClassCastException "异常 例 ; 例 : 判断对象是否是此类,或此类的子类 , 为了保证向下转型的正确性 例 : Object类的基本概念 取得对象信息 对象比较 java中只有一个类是没有继承关系的, 这个类就是Objec类 例 : class Person extends Object { } Object obj = new int[] {2, 3, 4} ; Object是一个万能的数据类型, 开发时用可以用Object接收任意数据类类 toString 编写简单java类是要获取对象信息 覆写toString即可 是Object自带的一个方法 例 : public boolean equals (Object obj) 比较两个对象的内容是否完全一致 默认情况该方法只是进行了两个对象的地址判断 要判断内容需要对该方法覆写 例 : 抽象类的基本概念 抽象类的相关说明 模板设计模式 实际开发中很少继承已经完善的类, 进行父类设计时应优先考虑抽象类 主要作用 : 对子类中覆写方法进行约定, 定义一些抽象方法, 使用原则 : 抽象类要有子类 抽象类的子类(不是抽象类)一定要覆写抽象类的全部抽象方法 抽象类的对象实例化可以利用对象多态性通过子类向上转型完成 使用absract 定义抽象类和抽象方法 使用问题 抽象类不能直接实例化 使用抽象类时往往时是为了解决代码重复的问题 不能用final关键字定义 (抽象类必须有子类, final不能有子类) 抽象类的组成是在普通类的基础上扩展的, 提供构造方法, 也 允许没有构造方法 抽象类中可以有static方法, 该方法不受抽象类局限, 可以用类名称进行调用 实际应用 定义一个抽象类, 有"机器人" , "人", "猪" 三个字类, 抽象类定义"eat"抽象方法, "人"的eat()跟"机器人"的eat()行为是不一样的, 在各自的类中写eat(), (即对一样抽 象的东西进行统一处理) 抽象类好处 对子类方法统一管理 自身可以提供一些普通方法, 普通方法可以调用抽象方法, 这些抽象方法在有子类提 供实现时才会生效 基本数据类型进行包装处理后可以像对象一样引用传递 包装类实现原理 包装类对象型(Object直接子类) 包装类数值型(Number直接子类) Boolean, Character Byte, Short, Integer, Long, Float, Double public byte byteValue() 从包装类获取byte基本数据 public short shortValue() public abstract int intValue() public abstract long longValue() public abstract float floatValue() public abstract double doubleValue() Number类中的方法 装箱与拆箱 Integer obj = new Integer(10) ; //装箱 int num = obj. intValue () ; //拆箱 以int 和 Interger为例 JDK1.9后, 所有包装类中提供的构造方法变为了过期处理, 不建议用户继续使用了, 因为JDK1.5后提供了自动的装箱与拆箱处理 Integer obj = 10 ; //自动装箱,拆箱, 不再关心构造方法 Object obj = 19.2 ; //double自动装箱为Double, 向上转型为Object double num = (Double) obj ; //向下转型为包装类, 再自动拆箱 自动装箱最大好处 注意 相等判断包装类相等判断要用equals()完成 接口的基本定义 抽象类与普通类相比最大的优势在于: 可以实现对子类覆写方法的控制,但是在抽 象类里面可能依然保留有一些普通方法, 而普通方法里面可能会涉及到一-些安全或 者隐私的操作问题,开发中如果要对外部隐藏一些细节,则就可以通过接口来进行 描述。 Java中接口主要用interface关键字定义 使用原则 接口要被子类实现(implements), 一个子类可以实现多个接口 子类(如果不是抽象类)一定要覆写接口中的全部抽象方法 接口对象可以利用子类对象的向上转型进行实例化 子类可继承抽象类并且实现接口(要先继承再实现) 一个接口可以继承若干个父接口 重要说明 接口可以通过Object接收 接口中的所有抽象方法的访问权限都是public 接口定义加强 JDK1.8之前, 最初不会让子类直接实现接口, 而是在中间追加一个过渡的抽象类(因 为不能保证接口的完善, 如果接口改动, 所有子类覆写的方法都要改) 全部由抽象方法和全局常量组成 JDK1.8之后, 为了解决以上缺陷, 允许接口中定义普通方法或static方法 接口中的普通方法必须用default声明 但还是该操作属于挽救方法, 不是必须不建议使用 接口开发中应该奉行 : 接口就是抽象方法 使用接口定义的标准 工厂设计模式 代理设计模式 抽象类与接口区别 例如 usb键盘接口, usb鼠标接口, 电脑不管插入的是键盘还是鼠标,只要符合usb标 准, 都能正常识别 所有子类都放在Factory类里, 在一个类里实现所有功能, 主类(客户端)只关注IFood, 而不关注Food是Bread还是Milk Ieat eat = new EatProxy(new Eat()) ; eat.get() ; //吃东西 EatProxy()包括准备食材, 做饭, 收拾碗筷等方法, 但主类只关心是不是真的Eat()了, 而不关心"Eat的代理" 代理主题负责真实主题的准备业务与收尾, 没有代理主题无法实现真实主题 定义关键字 abstract class 抽象类名称 {} interface 接口名称 {} 组成 抽象类 : 构造, 普通方法, 静态方法, 静态常量, 普通成员 接口 : 抽象方法, 全局常量, 普通方法, static方法 权限 子类使用 抽象类 : 可以使用各种权限定义 接口 : 只能使用public 抽象类 : 子类通过extends关键字可以继承一个抽象类 接口 : 子类使用imprements关键字可以实现多个父接口 两者关系 使用 抽象类可以实现若干个接口 接口不允许继承抽象类, 但是允许继承多个父接口 抽象类或接口要定义子类 子类一定要覆写抽象类或接口中的全部抽象方法 通过子类的向上转型实现抽象类或接口对象的实例化 两者都能使用的情况下优先考虑接口, 因为接口可以避免子类的单继承局限, 另外也需要先从接口进行整体设计 JDK1.5后, 泛型是为了解决"ClassCastException"的问题(对象向下转型的风险) 泛型问题的引出 Object范围太广了, 是造成泛型产生的主要依据 泛型的基本定义 参数的返回值类型可以由对象实例化时动态决定, 需要在类定义时定义占位符(泛型 标记) 如果未设置泛型类型会自动使用Object作为类型, 但编译时会提示警告信息 泛型定义完成后可以在实例化时进行泛型类型的设定 如 : Point<Interger> point = new Point<interger>() ; //必须用包装类, 不能用 int 这样point对象里所有泛型都会是Int类型(仅局限与此对象当中) JDK1.7后, 可以简化为 :Point<Interger> point = new Point<>() ; 注意 : 不能局限数据类型对程序很危险, 开发时一般使用泛型操作, 不用Object 泛型通配符 可以接收所有的泛型类型, 并且不能修改里面的数据(允许获取),通配符"?" 例 : ?extends 类 : 设置泛型的上限 ?extends Number 表示该泛型类型只能设置Number或Number的子类 ?super 类 : 设置泛型的下限 ?extends String表示该泛型只能用String或其父类 例 : 例 : 泛型接口 泛型也可以在接口中定义 两种实现方式 class MessageImpt implements IMessage<String> {} interface IMessage<T> { } class MessageImpt<S> implements IMessage<S> {} 例 : 例 : 泛型方法 泛型方法不一定要在泛型类中 例 : 利用包实现类的包装, 实际开发中, 所有的类都必须放在包里面 包的定义 包的导入 静态导入 生成jar文件 系统常用包 访问控制权限 为了类的方便管理, 而且很难包证类的不重复, 所以将类放在不同的目录中, 这个目 录就是包 package cn.mldn.demo ; 打包编译处理 javac -d . Demo.java 将 *.class文件保存在定义的目录中 "-d": 表示要生成的目录, 目录的结构就是package定义的结构 "." : 表示程序类文件生成在当前目录中 定义包, 点表示分割子目录(子包) java cn.mldn.demo.Demo 带着包执行程序类 不同包中的类存在有互相调用的关系, 这时就需要用import语句来导入其他包中的 程序类 例 : package cn.mind.util ; public class Message { public String getContent() { return "这是一条消息" ; } } package cn.mind.demo ; import cn.mind.util.Message ; //导入其他包的类 public class Demo { public static void main(String args[]) { Message msg = new Message() ; System.out.println(msg.getContent()) ; } } javac -d . *.java 编译所有文件, 自动决定编译顺序 注意public class与class区别 public class 一个*.java文件里只允许有一个public class, 定义为public class才能被其他包使用 class 类名称可以与文件名称不一致, *.java里可以有多个, 但是外包无法访问 开发标准 : 一个*.java里只提供一个程序类 包名称必须用小写 还有一个形式 : import cn.mind.util.* " 包.* "的导入形式并不是全部加载, 只会加载有被调用的包 ".*"的方式只导入下一级的类, 再下一级的类要另外导入 用包.*和用具体的类性能是相同的 不同的包有同名的类时会出现引用不明确 解决 : 调用时用完整名称, cn.mind.util.Message msg = new cn.mind.util.Message() JDK1.5后对于全是static方法的类可用静态导入 import static cn.mind.util.Message.* ; 作用 : 该类中的static方法可由主方法直接调用 但是很少有人这么写, 很不方便 一个项目有大量的*.class文件, 把相关的文件用文件管理打包成一个压缩文件结构, 这样的结构就是jar文件 jar -cvf demo. jar cn "-c" : 创建一个新的jar文件 "-v" : 得到一个详细输出 "-f" : 设置要生成的jar名称(demo.jar) jar的使用 每个jar文件都是一个独立的文件路径 SET CLASSPATH = . ;D:\Code\Java\demo.jar 我的电脑不知道为什么没其作用? cmd窗口配置临时环境变量(窗口关闭会恢复默认) 编译时会从demo.jar目录里查找*.class文件 *.jar没有配置正确会出现错误 java.lang.ClassNotFoundException: cn.mind.demo.Demo JDK1.9后的模块化操作(了解概念) JDK1.9之前提供的是一个所有类的*.jar文件(rt.jar、tools.jar, 需要在CLASSPATH 里配置这两个文件), 那么启动Java虚拟机就需要加载这两个几十兆的类文件 JDK1.9后的模块化(Module)设计 : 启动时根据程序加载指定的模块(模块中有包), 启动速度变快了 Java自身提供的(除了JDK提供类库外还会有有一些标准) 第三方厂商提供的 常见 java.lang String, Number,Object等类里面, 该包自动默认导入 java.lang.reflect 反射机制处理包, 所有的设计由此开始 java.util 工具类的定义, 包括数据结构的定义 java,io java.net 输入与输出流操作的程序包 网络程序开发的程序包 Java.sql 进行数据库编程的开发包 java.applet Java最原始的使用形式, 直接嵌套在网页上执行的程序类 现在的程序已经以Application为主了(有主方法的程序类) java.awt, javax.swing java的图形界面开发包(GUI), 其中awt是属于重量级的组件, swing是轻量级组件 private default(缺省(早期直译), 即不写) protected public 同一包中的同一类, 同一包中的不同类 同一包中的同一类 同一包中的同一类, 同一包中的不同类, 不同包的子类 同一包中的同一类, 同一包中的不同类, 不同包的子类, 不同包的所有类 共有四种权限 权限使用参考方案 属性定义全部使用private 方法定义全部使用public 类图 时序图 用例图 UML类图是统一的建模语言, 本质是利用图形化的形式来实现程序类关系的描述(现 在很多人都不使用类图了,都是写完程序再转换出来) 往往三层结构 类名称 属性 方法 普通类直接编写, 抽象类使用 斜体 , 往往再加上"abstract" 使用"访问权限 属性名称 : 属性类型" 的格式 访问权限的描述: public(+), protected(#), private(-) 使用"访问权限 方法名称(): 返回值" 的结构描述 工具 可以使用PowerDesigner 描述代码的执行流程 描述的是程序的执行分配(不同用户具备不同功能) 项目设计过程中出现得比较多 单例设计 多例设计 要求一个类只允许提供一个实例化对象 例 : 懒汉式 饿汉式 特点 : 构造方法私有化, 类内部提供static方法获取实例化对象 在系统加载类时就会自动提供类的实例化对象, 之前定义类都是懒汉式 后面要考虑线程同步问题 可以保留多个实例化对象, 构造方法也是私有化 例 : Java在JDK1.5后才提出枚举, 主要作用是用于定义有限个对象的一种结构, 枚举就属 于多例设计, 并且比多例设计简单(enumeration [ɪˌnuːməˈreɪʃn] ) 定义枚举类 enum关键字 例 : switch中的枚举项判断 例 : Enum类 定义枚举结构 枚举应用案例 JDK1.5后提供的关键字用于定义枚举类 是一个抽象类, 使用enum关键字定义的类就默认继承了此类 public final String name() public final int ordinal() 方法 获得对象名字 获得对象序号 例 : 与正常类一样可以定义私有属性, 构造方法, 也可以实现接口, 覆写方法等等 不一样的是枚举可以直接定义抽象方法, 要求每个枚举对象都要独立覆写此抽象方 但是这个好像没什么用处 枚举的定义非常灵活, 但是更多时候建议使用它的正确用法, 就是定义一个范围, 实 例对象即可 其实不用枚举也能实现 认识异常对程序的影响 处理异常 可处理多个异常 异常处理流程 throws关键字 throw关键字 异常处理模型 RuntimeException 自定义异常类 assert断言 异常指导致程序中断执行的指令流 出现错误会中断程序执行 try, catch, finally几个关键字 基本处理结构 例 : printStackTrace()方法可以获得完整异常信息 产生异常 自动实例化相关异常对象 存在异常处理? true false try捕获实例化对象 catch匹配? true false 处理异常 catch 2匹配? true false 打印异常信息 程序中断执行 处理异常 执行final代码 已处理? Stop true false 其他代码 内存简单分析 new per, an person, Animal 内存空间 Error Exception 编译时异常, 程序未执行出现的异常, 开发者无法处理 程序执行中出现的异常, 开发者可处理 可用Exception处理所有异常 catch (Exception e) { } 但是Exception描述的异常信息不明确 一般将捕获范围大的异常类型放后面 是在方法上定义使用的, 将此方法可能产生的异常告诉使用者, 由使用者处理 public void fun() throws Exception { } 在代码块中使用的, 手工产生一个异常类的实例化对象, 并且进行异常的抛出处理处 try { throw new Exception("抛出的异常信息") ; } catch (Exception e) { } 与Exception区别 RuntimeException是Exception子类 RuntimeException标注的异常不需要强制性处理, 而Exception必须强制处理 常见RuntimeException异常 NumberFormatException, ClassCastException, NullPointException 开发中只要是用throws定义的方法都必须要求开发中进行手工处理, 这样编写太麻 烦了, 所以提供灵活可选的异常处理父类"RuntimeException", 该类的子类不需要 强制性处理 两种方案 继承Exception 继承RuntimeException 例 : JDK1.4后追加的功能, 确定代码执行到某一行时所期待的结果, 程序中没有强制执 行, 只是一种检测手段 要想执行断言, 运行要时加 "-ea" java -ea Demo 例 : 内部类的基本概念 内部类的相关说明 static定义内部类 方法中定义内部类(局部内部类) 匿名内部类 在一个类的内部定义其他的类, 这样的类成为内部类 例 : 目的是为了让内部类可以直接访问外部类的私有属性 , (如果写在外部,则需要实例化 外部类, 调用getter方法才能访问私有属性,比较麻烦) 同样外部类也可以轻松访问内部类的私有属性 在外部也可以产生内部类的实例化对象, 格式 : 外部类.内部类 内部类对象 = new 外部类().new 内部类() ; 编译完会形成一个 "外部类$内部类.class" 类文件 因为内部类可以访问外部类的私有属性, 内部类实例化时一定要保证外部类已经实 例化了 让内部类只允许外部类访问 使用private定义类 内部接口 抽象类与接口中都可以定义内部接口 JDK1.8后, 接口中追加了static, 不受实例化对象的控制, 可以实现内部 利用类实现 接口 Outer.Inner inner = new Outer.Inner() ; 看到这个应该立即想到是static内部类 static定义内部接口(比static定义内部类常用) 用static定义内接口部是因为这些接口时一组相关的定义, 可以更明确的描述出这些 接口的主要目的, 功能(方便做统一管理) 例 : 例 : JDK1.8后内部类中才可以直接访问方法中的参数, 之前必须给参数加final, 取消这样 的限制是为了其扩展的函数式编程准备的 一种简化的内部类的处理形式, 主要在抽象类和接口上使用的, 是一个没有名字只能 使用一次,且结构固定的子类操作 如果一个子类只需要使用一次, 那么没必要定义成一个单独的类, 只需使用匿名内部 例1 : 例2(为了更方便的体现出匿名内部类的使用) : JDK1.8开始引入的, 函数式编程可以避免一些面向对象编程中一些繁琐的处理问题 Lambda表达式 方法引用 内建函数式接口 说明 比较著名的函数式编程语言 haskell Scala 可以简化代码 实现要求 SAM(Single Abstract Method), 即只允许一个抽象方法 格式 方法没有参数 : () ->{ } ; 方法由参数 : (参数, 参数) ->{ } ; 如果只有一行语句返回 : (参数, 参数) ->语句 ; JDK1.8后提供的, 不同的方法名称可以描述同一个方法, 但必须是函数式接口 引用静态方法 类名称 :: static 方法名称 引用某个实例对象的方法 引用形式 实例化对象 :: 普通方法 引用特定类型的方法 特定类 :: 普通方法 引用构造方法 类名称 :: new 为了方便直接引用系统中提供的函数式接口 java.util.function开发包 功能型函数式接口() 例 : 消费型函数接口 只能进行数据处理操作, 而没有任何的返回 例 : 供给型函数式接口 没有接收参数, 但是有返回值 例 : Supplier<T> T get() Consumer<T> void accept​(T t) Function<T,​R> R apply​(T t) 有接收参数, 也有返回 断言型函数式接口 进行判断处理 例 : Predicate<T> 链表实现简介 数据增加 获取集合个数 空集合判断 返回集合数据 根据索引取得数据 链表(修改指定索引数据) 链表(判断数据是否存在) 链表(数据删除) 链表(清空链表) 链表的本质是一个 动态的对象数组 , 它可以实现若干个对象的存储, 利用引用的逻辑 关系来实现类似于数组的数据处理操作 由于传统的数组长度是固定的, 依赖于角标的控制, 应用很有限(数组的接收及循环处 理) 例(过程) : 数组根据索引获取数据的时间复杂度是1, 而链表为n, 见例 例 : Eclipse简介 使用JDT开发Java程序 代码调试 junit测试工具 bin 保存所有的*.class文件, 这些文件会自动进行编译处理 src 保存所有*.java源文件 切换UTF-8编码 Window → Preference → Workspace 工作空间 常用快捷键 Ctrl + 1 代码纠正提示 Alt + / 进行代码提示 Ctrl + / 单行注释 Ctrl + Shift + O 自动导入需要的包 Ctrl + Shift + 查看快捷键 source菜单栏 包括自动生成setter, getter方法 覆写toString ...... 将类导出为*.jar文件 File → Export 导入*.jar文件 项目上右键 → 属性 → JavaBuid Path → Libraries → ClassPath 设置断点 使用调试模式 调试工具 F5 : 单步跳入 F6 : 单步跳过 F7 : 单步返回 F 8 : 恢复执行 白盒测试和黑盒测试之外, 还有用例测试 ,JUnit就是用例测试 ...... 类变量也称为静态变量,在类中以 static 关键字声明,但 必须在方法之外 例 : 实现思路 1.定义接口ILink<E>, 添加操作链表的各种方法 2.创建类LinkImple<E>实现接口ILink<E> 3.创建私有内部类Node<E>用于保存数据E data;, 和下一个节点Node next; 4.ILink<E>创建用于操作链表的方法(以add(E data)为例) 1.判断data是否为null 2.实例化一个Node, newNode 3.根节点root保存 4.内部类Node里创建方法addNode(Node newNode)用于保存节点 简单工厂设计模式 工厂方法设计模式 抽象工厂设计模式 例 : 例 : 例 : 1、抽象产品 2、具体产品 3、具体工厂 4、产品使用者 抽象产品类 具体产品类 抽象工厂类 具体工厂类 角色:和工厂方法一样 34.IntelliJ IDEA开发工具 同一种类型的一组数据, 按照一定顺序排列, 只有一个名字, 按照编号方式管理数据 创建数组开辟一整块连续空间, 数组名指向该空间首地址(多维数组即一个数组里存 放另一个数组的首地址) byte : 0 ; int : 0 ; boolean : false ; char : "\u0000"(空字符) ; 引用数据类型 : null ; 例 : 二维数组遍历 例 : 可以 : 数组类型 数组名称 [][] = new 数据类型 [行数][] ; //只固定行数 数组类型 [] 数组名称 [] = new 数据类型 [行数][列数] ; 二维数组打印杨辉三角形 例 : 其他 面向过程(POP), Procedure Oriented Programming 是一种以过程为中心的编程思想, 就是分析出解决问题所需要的步骤,然后用函数 把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。 7.其他 高内聚 尽可能类的每个成员方法只完成一件事(最大限度的聚合) 低耦合 减少类内部,一个成员方法调用另一个成员方法。 3.MVC设计模式 MVC是常用的设计模式之一,将整个程序分为三个层次:视图模型层,控制器层, 与 数据模型层。 数据模型层 Model 主要处理数据 数据对象封装 : model.bean/domain 数据库操作类 : model.dao 数据库 : model.db 视图层 View 显示数据 相关工具类 : view.utils 自定义view : view.ui 控制层 Controller 处理业务逻辑 应用界面相关 : controller.activity 存放fragment : controller.fragment 显示列表的适配器 : controller.adapter 服务相关的 : controller.service 抽取的基类 : controller.base JavaBean JavaBean是一种Java语言写成的可重用组件, 是一个类(就是一个简单的类标准起了 个名叫JavaBean) 标准 类是公共的 有一个无参的公共的构造器 有属性,且有对应的get、set方法 常用于数据库的对接 例 : View → Controller → Model → DataBase controller model view UserService User UserManager Test UserView 注意 不要仅为了获取其他类的某个功能而继承, 要考虑其中关系 同一个工程 同一个包, 不同子类 同一个包 Java只支持单继承 子类不能访问父类中的private属性和方法 导入第三方开发包 Project → Properties(属性) → Java Build Path → Libraries → ClassPath →Add External JARs 子类抛出的异常级别不能高于父类抛出异常 例 : 返回值 父类返回引用数据类型, 子类只能返回其引用数据类型及其子类 父类是基本数据类型, 子类返回值类型必须相同 引用父类属性, 父类构造, 父类方法 访问属性 如果本类中没有, 则从父类中查找 访问方法 如果本类中没有, 则从父类中查找 父类的引用指向子类的对象 例 : Java引用变量有两个类型 编译时类型 运行时类型 属性是在编译时确定的(声明的类型),而方法的调用是在运行时确定的(new出的类) 动态绑定 虚拟方法调用 只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体 方法,这称“动 态绑定” 静态绑定 对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法, 这称“静 态绑定” 面试题 : 覆写与重载区别 对File, String, Data, 以及包装类(Wrapper Class)来说, 比较的是内容而不考虑是否 是同一个对象, 因为在这些类中重写了Object中的equals方法 equals()覆写步骤 2.判断是否属于要进行比较的类, instanceof 3.判断是否为null 4.强制类型向上转换, 并比较所有属性是否都相等 1.判断两个对象的地址值是否相同, 是就返回true 面试题 "==" 与equals区别 "=="是一个操作符, equals是一个方法 "=="比较基础数据类型时比较的是数据的值, 比较引用数据类型比较的是地址, equals()是Object类的方法, 只可以比较引用数据类型(默认地址值) 子主题 重载和重写都是实现多态的方式, 重载实现编译时的多态性, 重写实现运行时的多态 重载发生一个类里面 , 同名的方法有不同的参数列表(参数数量, 参数类型) 重写发生在子类和父类之间 , 子类重写的方法与父类的方法有相同的返回值类型, 修饰符的访问权限要大于被重写的方法 重写方法抛出的异常不能比父类抛出的要宽泛 方法 clone() hashCode() 两个对象hashCode()相等, 这两个对象equals().. ...... finalize() notifyAll() notify() Object o = true?new Integer(1):new Double(2.0); //编译时"1"就转为了Double //打印o为1.0 static成员特点 随着类的加载而加载 优先于对象存在 修饰的成员, 被所有对象所共享 访问权限允许时, 不用创建对象可以直接被类调用 执行顺序 父类static代码块 → 子类static代码块 →父类普通代码块 → 父类构造 →子类普通 代码块 → 子类构造 例 : 包装类之间的比较 子主题 定义的常量默认都是全局常量(static final), 不加static final也一样 应用场景 安全代理 远程代理 延迟加载 分类 静态代理, 动态代理(涉及后面反射的知识) 屏蔽对真实角色的直接访问 通过代理类处理远程方法调用(RMI) 先加载轻量级代理对象, 需要时再加载真实代理对象(如图片预览) 接口冲突 解决 接口名.super.方法名() ; 也可以重写 一个类实现了两个具有同名方法的接口 若一个接口中定义了一个默认方法,而父类中也定义了一个同名同参数的非 抽象方法,则不会出现冲突问题。此时遵守 :类优先原则 Test类 常量于常量的拼接存储在常量池, 只要其中有一个变量就存储在堆中