1. Java简介
1.1 语言起源
- Java是SUN1995年推出的一门高级编程语言
- 2004年,J2SE1.5发布,成为Java语言发展史上的又一里程碑
- 2009年,Oracle收购SUN,取得Java的版权
- 2011年,Oracle公司发布java7的正式版
1.2 Java语言重要特性之跨平台
- 跨平台:即不依赖于操作系统,也不依赖硬件环境。
- Java的跨平台的原理:Java的跨平台是通过Java虚拟机(JVM)来实现的。
1.3 Java的三大平台
| 平台 | 简介 |
|---|---|
| Java SE | Java SE(Java Platform,Standard Edition)。Java SE 以前称为J2SE。它是允许开发和部署在桌面、服务器、嵌入式环境和实时环境中使用的Java 应用程序。Java SE 包含了支持Java Web 服务开发的类,并为Java Platform,Enterprise Edition(Java EE)提供基础。 |
| Java EE | Java EE(Java Platform,Enterprise Edition)。这个版本以前称为J2EE。企业版本帮助开发和部署可移植、健壮、可伸缩且安全的服务器端 Java 应用程序。Java EE 是在Java SE 的基础上构建的,它提供Web 服务、组件模型、管理和通信API,可以用来实现企业级的面向服务体系结构(service-oriented architecture,SOA)和Web 2.0 应用程序。 |
| Java ME | Java ME(Java Platform,Micro Edition)。这个版本以前称为J2ME,也叫K-JAVA。Java ME 为在移动设备和嵌入式设备(比如手机、PDA、电视机顶盒和打印机)上运行的应用程序提供一个健壮且灵活的环境。Java ME 包括灵活的用户界面、健壮的安全模型、许多内置的网络协议以及对可以动态下载的连网和离线应用程序的丰富支持。基于Java ME 规范的应用程序只需编写一次,就可以用于许多设备,而且可以利用每个设备的本机功能。 |
| Java Card* | 支持一些Java小程序(Applets)运行在小内存设备(如智能卡)上的平台 |
2. Java基础部分(基本语法,Java特性等)
2.1 标识符
- 什么是标识符:
就是程序员在定义Java程序时,自定义的一些名字
- 标识符定义规则
- 标识符由26个英文字符大小写(a
zAZ)、数字(0~9)、下划线(_)和美元符号($)组成。- 不能以数字开头,不能是关键字
- 严格区分大小写
- 标识符的可以为任意长度
- 标识符命名规范
- 包名: 多个单词组成时所有字母小写(例:package com.zeng)
- 类名和接口: 多个单词组成时所有单词的首字母大写(例:HelloWorld)
- 变量名和函数名: 多个单词组成时第一个单词首字母小写,其他单词首字母大写(例:lastAccessTime、getTime)。
- 常量名: 多个单词组成时,字母全部大写,多个单词之间使用(_)分隔(例:INTEGER_CACHE)
2.2 常量和变量
2.2.1 常量
常量的分类
- 整数常量:所有整数
- 小数常量:所有小数
- 布尔常量:只有true和false
- 字符常量:使用‘’引起来的单个字符
- 字符串常量:使用“”引起来的字符序列
- null常量:只有一个值null
char类型
- char类型表示的是单个字符类型。
- 注意:特殊字符的转义序列:转义字符
- 特殊字符使用”\”把其转化成字符的本身输出,那么使用”\”的字符称作为转义字符。
- 常见转义字符
- 转义字符 名称 Unicode
- \b Backspace (退格键) \u0008
- \t Tab (Tab键盘) \u0009
- \n Linefeed (换行) \u000A
- \r Carriage Return(回车) \u000D
- \ Backslash (反斜杠) \u005C
- ' Single Quote (单引号) \u0027
- " Double Quote (双引号) \u0022
Boolean类型
2.2.2 变量
变量的概述 用于存储可变数据的容器。
- 变量的数据类型
- 整型
- byte 代表一个字节的大小 8bit -128~127
- short 代表两个字节的大小 16bit -2^(15)~2^(15)-1
- int 代表四个字节的大小 32bit -2^(31)~2^(31)-1
- long 代表八个字节的大小 64bit -2^(63)~2^(63)-1
- 如果一个数值没有采取特殊的处理,那么该整数默认的类型是int。
- 可以使用数值后面添加L或小写l改变默认的整数类型。
- 浮点型
- float 代表四个字节的大小 32bit
- double 代表八个字节的大小 64bit
- java程序中所有的小数默认的类型是double类型,所以需要使用特殊的符号改变默认的小数类型。
- 字符型
- char 代表两个字节的大小 16bit
原理:将字符映射为码表中对应的十进制数据加以存储。- 布尔型
- boolean 占一个字节。只有true与false两个值。
- 变量的声明
- 格式: 数据类型 变量名字1 , 变量名字2 ,……, 变量名字n;
- 备注:变量名的首字母都一般都是以小写字母开始。
- 变量的初始化
- 初始化方式1: 数据类型 变量名字 = 数值。
- 初始化方式2: 数据类型 变量名字 , 变量名字 = 数值
2.3 数据类型
- 基本数据类型
- 引用数据类型
- 数据类型的转换
- 自动类型转换(也叫隐式类型转换)
- 要实现自动类型的转换,需要满足两个条件
- 第一、两种类型彼此兼容
- 第二、目标类型取值范围必须大于源类型
- 所有的数字类型,包括整形和浮点型彼此都可以进行转换。
- 要实现自动类型的转换,需要满足两个条件
- 强制类型转换(也叫显式类型转换)
- 当两种类型彼此不兼容,或者目标类型取值范围小于源类型(目标是byte,源是int)无法自动转换,此时就需要进行强制类型转换。
- 表达式的数据类型自动提升
- 算术表达式,逻辑表达式
- 所有的byte型、short型和char的值将被提升到int型。
- 如果一个操作数是long型,计算结果就是long型;
- 如果一个操作数是float型,计算结果就是float型;
- 如果一个操作数是double型,计算结果就是double型。
- java基本数据类型转换
- 自动类型转换(也叫隐式类型转换)
2.4 运算符
-
算数运算符
-
赋值运算符
运算符 运算 范例 结果 = 赋值 a=3,b=2 a=3,b=2 += 加等于 a=3,b=2;a+=b; a=5,b=2; -= 减等于 a=3,b=2,a-=b; a=1,b=2; *= 乘等于 a=3,b=2,a*=b; a=6,b=2 /= 除等于 a=3,b=2,a/=b; a=1,b=2; %= 模等于 a=3,b=2,a%=b; a=1,b=2 -
比较运算符
运算符 运算 范例 结果 == 相等于 4 == 3 false != 不等于 4 != 3 true < 小于 4 < 3 false 大于 4 > 3 true <= 小于等于 4 <= 3 false >= 大于等于 4 >= 3 true instanceof 检查是否是类的对象 "hello" instanceof String true - 注意的细节
使用比较运算符的时候,要求两种数据类型必须一致
byte、short、char 会自动提升至int
- 注意的细节
-
逻辑运算符
-
注意
- “&”和“&&”的区别
单与时,左边无论真假,右边都进行运算;双与时,如果左边为真,右边参与运算,如果左边为假,那么右边不参与运算。 - “|”和“||”的区别
同理,双或时,左边为真右边不参与运算。 - “ ^ ”异或与“|”或的不同之处
当左右都为true时,结果为false
- “&”和“&&”的区别
-
位运算符
位运算符 运算符含义 & 与(AND) | 或(OR) ^ 异或 ~ 取反
当参与取反的数值是正数时,把对应的值加上负号,再-1;
当参与取反的数值是负数时,把对应的值加上负号,再-1;
负数的表现形式就是对应的正数取反,再加1。负数的最高位肯定是1. -
移位运算符
运算符 运算 范例 注意 << 左移 3 << 2 --> 3*2*2=12 空位补0,被移除的高位丢弃,空缺位补0 >> 右移 3 >> 1 --> 3/2=1 被移位的二进制最高位是0,右移后,空缺位补0; 最高位是1,空缺位补1 >>> 无符号右移 3 >>> 1 --> 3/2=1 被移位二进制最高位无论是0或者是1,空缺位都用0补 -
三元运算符
格式 (条件表达式) ? 表达式1 :表达式2;
-
运算符的优先级与结合性
2.5 流程控制语句
- if else
- switch
- switch语句选择的类型只有四种:byte,short,int, char。
- jdk 1.7允许switch中有String变量和文本
- case之间与default没有顺序。先判断所有的case,没有匹配的case执行default。
- switch语句停止的条件是遇到了break关键字或者结束switch语句的大括号。
- 如果匹配的case或者default没有对应的break,那么程序会继续向下执行,运行可以执行的语句,直到遇到break或者switch结尾结束。
- switch case中的值必须要与switch表达式的值具有相同的数据类型。而且case后跟的值必须是常量,不能跟变量。
- switch语句选择的类型只有四种:byte,short,int, char。
- while,do while
- for循环
- for和while区别
- for里面的两个表达式运行的顺序,初始化表达式只读一次,判断循环条件,为真就执行循环体,然后再执行循环后的操作表达式,接着继续判断循环条件,重复整个过程,直到条件不满足为止。
- while与for可以互换,区别在于for为了循环而定义的变量在for循环结束时就在内存中释放。而while循环使用的变量在循环结束后还可以继续使用。
- 最简单无限循环格式:while(true) , for(;;),无限循环存在的原因是并不知道循环多少次,而是根据某些条件来控制循环。推荐使用while(true)
- for和while区别
- break、continue关键字
方法(函数)
- 格式
修饰符 返回值类型 函数名(参数类型 形式参数1,参数类型 形式参数2,…)
{
执行语句;
return 返回值;
}
返回值类型: 运行这段程序得出的一个运算结果类型,如果函数没有返回值,则用void来表示该函数没有返回值。
函数名:仅仅是一个标识符,可以随意起名字。
形式参数:是一个变量,用于存储调用函数传递进来的实际参数。
实际参数:传递给形式参数的具体数值。
返回值: 返回给调用者。
- 特点
- 定义函数可以将功能代码进行封装
- 便于对该功能进行复用
- 函数只有被调用才会被执行
- 函数的出现提高了代码的复用性
- 对于函数没有具体返回值的情况,返回值类型用关键字void表示,那么该函数中的return语句如果在最后一行可以省略不写。
- 注意:函数中只能调用函数,不可以在函数内部定义函数。
- 定义函数可以将功能代码进行封装
- 重载
- 定义
在同一个类中,有一个以上的同名函数,只要函数的参数列表或参数类型不一样即可,与返回值无关, 这些统称为方法的重载。
- 原因
为了增强方法的阅读性,优化了程序设计。
- 定义
2.6 数组
2.7 注释
- 注释的作用
通过注释提高程序的可读性,使java程序的条理更加清晰,易于区分代码行与注释行。另外通常在程序开头加入作者,时间,版本,要实现的功能等内容注释,方便后来的维护以及程序员的交流。
- 注释的种类
- 单行注释(line comment)用//表示,编译器看到//会忽略该行//后的所有文本
- 多行注释(block comment)用/**/表示,编译器看到/*时会搜索接下来的*/, 忽略掉/**/之间的文本。
- 文档注释用/** */表示,是java特有的注释,其中注释内容可以被JDK提供的工具 javadoc 所解析,生成一套以网页文件形式体现的该程序的说明文档。
- 文档注释
- 需要使用sun给我们提供的javadoc工具生成一个html的说明文档。
- 只能抽取public的属性或者方法内容。
- 格式: javadoc –d 指定存储文档的路径 -version –author(可选) 目标文件
3. 关键字
3.1 常见的关键字
3.2 关键字分类
3.3 所有关键字
- 备注:goto是Java的保留关键字,意思是Java并没有使用goto,以后是否使用未定
4. 面向对象
4.1 面向对象思想
4.1.1 面向对象的描述
- 【面向对象】(Object Oriented,简称OO),是一种以事物为中心的编程思想;
- 【面向对象程序设计】(Object Oriented Programming,简称OOP),是一种程序开发的方法。它将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性、灵活性和扩展性。
- 【面向过程】(Procedure Oriented,简称PO)是一种以过程为中心的编程思想,强调的是功能行为。
4.1.2 面向对象的特征
- 【封装】(encapsulation):
把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或对象操作,对不可信的进行信息隐藏。
- 【继承】(inheritance):
继承是指一种能力:它可以使用现有的类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
- 【多态】(polymorphism)
定义时的类型和运行时的类型不一样,此时就成为多态。
是允许你将父对象设置成一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。
简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。
4.1.3 面向对象的五大基本原则
- 单一职责原则(SRP)
一个类的功能要单一,不能包罗万象
- 开放封闭原则(OCP)
一个模块在扩展性方面是开放的,对更改性方面是封闭的
- 里氏替换原则(LSP)
子类应当替换父类,并出现在父类能够出现的所有位置
- 依赖倒置原则(DIP)
具体依赖抽象,上层依赖下层
- 接口隔离原则(ISP)
模块间要通过抽象接口隔开,而不是通过具体的类强行耦合起来
4.2 类与对象
4.2.1 【类】
实际就是对某种类型事物的共同特性与行为的抽取
4.2.2 【对象】
在现实生活中存在的一个具体的事物
4.3 封装
封装的好处
- 隐藏了类的具体实现
- 操作简单
- 提高对象数据的安全性
4.4 继承
4.4.1 特点
- 描述类和类之间的关系
- 降低类和类之间的重复代码
4.4.2 extends关键字
4.4.3 继承细节
- 类名的设定:
被继承的类称之为父类(基类),继承的类称之为子类
- 子类并不能继承父类中所有的成员
- 父类定义完整的成员:静态成员、非静态、构造函数。静态变量和静态方法都可以通过子类名.父类静态成员的形式调用
- 所有的私有成员不能继承
- 构造函数不能继承
4.4.4 super关键字
- 作用
- 主要存在于子类方法中,用于指向子类对象中的父类对象
- 访问父类的属性
- 访问父类的函数
- 访问父类的构造函数
- 主要存在于子类方法中,用于指向子类对象中的父类对象
- 注意
- this和super很像,this指向的是当前对象的调用,super指向的是当前调用对象的父类。this和super只能在有对象的前提下使用,不能在静态上下文使用
- 子类的构造函数第一行会默认调用父类无参的构造函数,隐式语句
- 如果显式调用父类函数,编译器自动添加的调用父类无参构造函数就会消失。构造函数间的调用只能放在第一行,只能调用一次。super和this不能同时存在构造函数第一行
- this和super很像,this指向的是当前对象的调用,super指向的是当前调用对象的父类。this和super只能在有对象的前提下使用,不能在静态上下文使用
4.4.5 重写(override)
- 定义
在继承中,可以定义和父类名称相同且参数列表一致的函数,称之为函数的重写
- 前提
必须要有继承关系
- 特点
- 当子类重写父类的函数,那么子类的对象如果调该函数,调用的一定是重写后的函数;
- 可通过super关键字进行父类方法的调用;
- 子类可通过继承将父类的方法增强
- 当子类重写父类的函数,那么子类的对象如果调该函数,调用的一定是重写后的函数;
- 细节
- 函数名、参数列表必须相同
- 子类重写父类的函数时,函数的访问权限必须大于或等于父类的访问权限,否则编译报错
- 子类返回值类型必须是父类函数返回值类型或该返回值类型的子类,不能返回比父类更大的数据类型
4.4.6 子类对象查找属性或方法时的顺序
原则:就近原则 如果子类的对象调用方法,默认先使用this进行查找,如果当前对象没有找到属性或方法,找当前对象中维护的super关键字指向的对象,如果还没有找到则编译报错,找到直接调用
4.4.7 重载和重写的区别
- 重载
前提 所有重载函数必须在同一个类中
特点 函数名相同,参数列表不同,与其他无关(访问控制符、返回值类型)
不同 个数不同、顺序不同、类型不同 - 重写
前提 继承
特点 函数名、参数列表必须相同;子类的返回值类型要等于或小于父类的返回值类型
4.4.8 instanceof关键字
- 作用
比较运算符:用来判断一个对象是否属于指定类的对象
- 用法 对象 instanceof 类
- 注意
使用instanceof关键字做判断时,两个类之间必须有关系
4.4.9 final关键字
- 修饰成员属性
说明成员属性是常量,不能被修改,且必须赋初值
- 修饰类
- 该类是最终类,不能被继承
- 防止代码功能被重写
- 该类没有必要扩展
- 该类是最终类,不能被继承
- 修饰方法
- 该方法是最终方法,不能被重写
- 当一个类被继承,那么所有的非私有函数都将被继承,如果函数不想被子类继承并重写,可以用该final修饰
- 当一个类中的函数都被修饰为final时,可以将类定义为final的
- 编译时尝试将该函数内联,提高运行效率
- 该方法是最终方法,不能被重写
- 修饰形参
- 当形参被修饰为final,那么该形参在所属的方法中不能被修改
- 项目中主要用于一些只用来遍历未知数据的函数。将位置变量声明为final的。增强数据的安全性
- 当形参被修饰为final,那么该形参在所属的方法中不能被修改
4.4.10 Java取消多继承
- 多继承虽然能使子类同时拥有多个父类的特征,但其缺点也是显著的,主要有两方面
- 如果在一个子类继承的多个父类中拥有相同名字的实例变量,子类在引用该变量时将产生歧义,无法判断应该使用哪个父类的变量;
- 如果在一个子类继承的多个父类中拥有相同方法,子类中又没有覆盖该方法,那么调该方法时将产生歧义,无法判断应该调用哪个父类的方法
- 如果在一个子类继承的多个父类中拥有相同名字的实例变量,子类在引用该变量时将产生歧义,无法判断应该使用哪个父类的变量;
- 注意
- 子类将继承父类所有的数据域和方法。静态方法可以继承,但是不能重写;私有方法可以继承,只是不能调用
- java类是单继承,接口可以实现多继承
- 子类将继承父类所有的数据域和方法。静态方法可以继承,但是不能重写;私有方法可以继承,只是不能调用
4.5 多态
- 多态的概念
所谓多态就是指一个类实例的相同方法在不同情形下有不同的表现形式,多态机制使具有不同内部结构的对象可以共享相同的外部接口
- 多态体现
- 父类和子类有相同的成员变量,多态下访问的是父类的成员变量;
- 当父类和子类具有相同的非静态方法(就是子类重写父类的非静态方法),多态下访问的是子类的成员方法;
- 当父类和子类具有相同的静态方法(就是子类重写父类的静态方法),多态下访问的是父类的静态方法
- 父类和子类有相同的成员变量,多态下访问的是父类的成员变量;
4.6 成员变量和局部变量
- 成员变量属于对象,它存储在堆内,堆内的实体,当没有引用指向其时,才垃圾回收清理;
- 局部变量存在栈内存中,当不再使用时,马上就会被释放。
4.7 匿名对象
- 概念
没有名字的实体,也就是该实体没有对应的变量名引用
- 用途
- 当对象对方法进行一次调用的时候,可以使用匿名对象对代码进行简化
- 匿名对象可以作为实参进行传递
- 当对象对方法进行一次调用的时候,可以使用匿名对象对代码进行简化
- 注意
匿名对象的属性无法获取,因为没有引用变量指向该对象
4.8 构造方法
- 构造方法与普通函数的区别
- 一般函数是用于定义对象应该具备的功能。而构造函数定义的是,对象在调用功能之前,在建立时,应该具备的一些内容,也就是对象的初始化内容。
- 构造函数是在对象建立时由JVM调用,对对象初始化。一般函数是对象建立后,当对象调用该功能时才会执行。
- 普通函数可以使用对象多次调用,构造函数只在对象创建时调用。
- 构造函数的函数名要与类名一样,而普通的函数只要符合标识符的命名规则即可。
- 构造函数没有返回值类型。
- 构造函数注意的细节
- 当类中没有定义构造函数时,系统会指定给该类加上一个空参数的构造函数。这个是类中默认的构造参数。当类中如果定义类构造函数,这时默认的构造函数就没有了
- 在一个类中可以定义多个构造函数,以进行不同的初始化。多个构造函数存在于类中,是以重载的形式体现的,因为构造函数的名称都相同
- 当类中没有定义构造函数时,系统会指定给该类加上一个空参数的构造函数。这个是类中默认的构造参数。当类中如果定义类构造函数,这时默认的构造函数就没有了
- 构造代码块
- 作用
给所有的对象进行统一的初始化
- 与构造函数的区别
构造代码块是给所有对象进行统一初始化,构造函数给对应的对象进行初始化
- 作用
- 静态代码块
随着类的加载而加载。只执行一次,优先于主函数。用户给类进行初始化
4.9 this关键字
- 概念
this关键字代表的是对象的引用。即this指向一个对象,所指向的对象就是调用该函数的对象引用。
- 注意
- this只能在非静态(没有static修饰的)函数中使用;
- 构造函数相互调用必须放在构造函数的第一个语句,否则编译报错;
- 可以解决构造函数中对象属性和函数形参的同名问题。
4.10 static关键字
- 作用
实现对象之间重复属性的数据共享
- 使用
主要用于修饰类的成员
- 成员变量
- 非静态成员变量:需要创建对象来访问
- 静态成员变量:使用类名直接调用,也可以通过对象访问
- 非静态成员变量:需要创建对象来访问
- 成员方法
- 静态函数
静态函数中不能访问非静态成员变量,只能访问静态变量
静态方法不可以出现this,super关键字
因为静态优先于对象存在 - 非静态函数
非静态函数中可以访问静态成员变量
- 静态函数
- 静态代码块
- 成员变量
- 特点
- 静态会随着类的加载而加载,随着类的消失而消失。声明周期很长
- 优先于对象而存在
- 被所有实例(对象)所共享
- 可以直接被类名调用
- 优缺点
- 优点:对对象的共享数据进行单独空间的存储,节省空间
- 缺点:生命周期过长,访问出现局限性(静态只能访问静态)
- 静态变量(类变量)和实例变量的区别
- 存放位置
- 类变量随着类的加载而加载存在于方法区中
- 实例变量随着对象的建立而存在于堆内存中
- 声明周期
- 类变量生命周期最长,随着类的消失而消失
- 实例变量生命周期随着对象的消失而消失
- 存放位置
- 应用
自定义数据工具箱
4.11 内部类
基础
- 成员内部类
- 访问方式
- 内部类可以直接访问外部类的成员属性和成员方法(包括private成员和静态成员)
- 外部类需要访问内部类的成员属性时,需要创建内部类的对象
- 在外部类的成员函数中创建内部类的对象,通过内部类对象直接访问内部类的成员
- 在其他类中直接创建内部类的对象:
- 第一种方式:Outer outer = new Outer(); Outer.Inner inner = outer.new Inner();// 必须通过Outer对象来创建
- 第二种方式:Outer.Inner inner1 = outer.getInnerInstance();
- 在外部类的成员函数中创建内部类的对象,通过内部类对象直接访问内部类的成员
- 如果要访问外部类的同名成员,需要以下面的形式进行访问
- 外部类.this.成员变量
- 外部类.this.成员方法
- 内部类可以拥有private访问权限、protected访问权限、public访问权限和包访问权限
- 优势
成员内部类作为外部类的成员,那么可以访问外部类的任意成员
- 特点
- 私有的成员内部类:不能在其他类中直接创建内部类对象来访问
- 静态的成员内部类:如果内部类中包含静态成员,那么Java规定内部类必须声明为静态的
- 访问静态内部类的格式:Outer.Inner in = new Outer.Inner()
- 访问方式
- 局部内部类
- 访问方式
可以直接在包含局部内部类的方法中创建局部内部类的对象,调用局部内部类的成员
- 特点
- 不能使用任何的访问修饰符
- 会生成两个.class文件,一个是Outer.class,另一个是Outer$LocalInner.class
- 局部内部类只能访问方法中声明的final类型的变量(可以访问获取值,但是不能修改,系统会默认添加final属性)
- 原因
- 因为局部内部类最终会被编译为一个单独的类,其所访问的局部变量会成为这个类的属性
- 如果访问的一个值类型局部变量,就会造成这个类的属性与所访问的局部变量不是同一个,会造成数据不同步
- 所以强制要求局部变量必须为final,避免数据不同步
- 访问方式
- 匿名内部类
就是没有类名字的内部类
一般使用匿名内部类的方法来编写事件监听代码
匿名内部类也是不能有访问修饰符和static修饰符的
匿名内部类是唯一一种没有构造器的类- 作用
简化内部类书写
- 前提:
必须继承一个类或者是实现一个接口
- 注意细节
使用匿名内部类时,如果需要调用匿名内部类的两个方法或者两个方法以上,可以使用变量指向该对象
- 作用
- 静态内部类
静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似,并且它不能使用外部类的非static成员变量或方法
深入理解内部类
- 为什么局部内部类和匿名内部类只能访问局部final变量?
背景:当test方法执行完毕之后,变量a的生命周期就结束了,而此时Thread对象的生命周期很可能还没有结束,那在再Thread的run方法中继续访问变量a就变成了不可能了,但是又要实现这样的效果,怎么办呢?Java采用了【复制】的手段来解决这个问题
方案:也就是说如果局部变量的值在编译期间就可以确定,则直接在匿名内部里面创建一个拷贝。如果局部变量的值无法在编译期间确定,则通过构造器传参的方式来对拷贝进行初始化赋值。
解决:java编译器就限定必须将入参变量限制为final变量
4.12 抽象类
当描述一个类的时候,如果不能确定功能函数如何定义,那么该类就可以定义为抽象类,功能函数应该描述为抽象函数
- 特点
- 有抽象函数的类,该类一定是抽象类
- 抽象类中不一定要有抽象函数
- 抽象类不能使用new创建对象
- 编译器强制子类实现抽象类父类的未实现的方法
- 可以不实现,前提是子类的也要声明为抽象的
- 优点
- 提高代码的复用性
- 提高代码的扩展性,便于后期的代码维护
- 构造函数
抽象类中一定有构造函数,主要为了初始化抽象类中的属性,通常由子类实现
- 注意细节
- 抽象类可以没有抽象方法
- 抽象类可以继承普通类与抽象类
- 抽象类不能直接使用类名创建实例,但是有构造方法,构造方法是让子类进行初始化
- 抽象类一定有构造方法
- abstract与其他修饰符的关系
- final与abstract不能共存:
final:它的作用是修饰类代表不可以继承,修饰方法不可重写;
abstract:修饰类就是用来被继承的,修饰方法就是用来重写的; - static与abstract不能共存:
abstract修饰的方法没有具体的方法实现,所以不能直接调用
static修饰的方法可以用类名调用 - private与abstract不能共存:
private修饰的只能在本类中使用
abstract方法是用来被子类进行重写的,有矛盾
- final与abstract不能共存:
4.13 接口
Java中的接口主要是用来拓展定义类的功能,可以弥补Java中单继承的缺点
- 接口的定义格式
interface 接口名{
属性;
抽象方法;
}
- 注意
接口中的所有属性,默认的修饰符是public static final
接口中的所有方法,默认的修饰符是public abstract - 接口的特点
- 类实现接口可以通过implements实现,实现接口的时候必须把接口中的所有方法实现,一个类可以实现多个接口;
- 接口中定义的所有属性默认是public static final的,即静态常量,所以定义的时候必须赋值;
- 接口中定义的方法不能有方法体,接口中定义的方法默认添加public abstract;
- 有抽象函数的不一定是抽象类,也可以是接口;
- 由于接口中的方法默认都是抽象的,所以不能被实例化;
- 对于接口而言,可以使用子类来实现接口中未被实现的功能函数;
- 如果实现类中要访问接口中的成员,不能使用super关键字。因为两者之间没有显式继承关系,况且接口中成员属性是静态的。可以使用接口名直接访问。
- 接口没有构造方法。
- 接口与类之间的关系
接口与类之间是实现关系。非抽象类实现接口时,必须把接口里面的所有方法实现。类与接口之间是可以多实现的(即一个类可以实现多个接口)。
4.14 接口和抽象类的区别
4.15 包机制
- 使用:
package 包名
- 包的优点
- 防止类文件冲突;
- 使源文件与类文件分离,便于软件最终发布。
- 注意细节
- 一个Java类只能定义在一个包中;
- 包语句肯定是描述类的第一条语句。
- 包机制引发的问题
有了包之后,每次访问类都需要把包名和类名写全。
- 解决办法: import 包名.类名
- 注意细节
from xx import *;
使用“*”不能导入包中子类包的class文件; import语句可以是多条的。
4.16 访问修饰符
- 修饰类成员
- 成员使用private修饰,只在本类中使用;
- 如果一个成员没有使用任何修饰符,就是default,该成员可以被包中的其他类访问;
- 成员被protected修饰,可以被包中其他类访问,并且位于同一个包中的子类也可以访问;
- public修饰的成员可以被所有类访问。
- 修饰类
- 类只有两种:public和默认(成员内部类可以使用private);
- 父类不可以是private和protected,子类无法继承;
- public类可以被所有类访问;
- 默认类只能被同一个包中的类访问。
4.17 jar包
jar包就是打包文件
jar包是一种打包文件java active file,与zip兼容,称之为jar包
- jar命令
- jar工具存放在jdk的bin目录中(jar.exe);
- jar工具:主要用于对class文件进行打包(压缩);
- dos中输入jar查看帮助
- 详细命令
jar cvf test.jar cn 1:jar cf test.jar cn 在当前目录生成test.jar 文件,没有显示执行过程 2:jar cvf test.jar cn 显示打包中的详细信息 3:jar tf test.jar 显示jar文件中包含的所有目录和文件名 4:jar tvf test.jar 显示jar文件中包含的所有目录和文件名大小,创建时间详细信息 5:jar xf test.jar 解压test.jar到当前目录,不显示信息 6:jar xvf test.jar 解压test.jar到当前目录,显示详细信息 7:可以使用WinRaR进行jar解压 8;将两个类文件归档到一个名为 test2.jar 的归档文件中: jar cvf test2.jar Demo3.class Demo4.class 9:重定向 1:tvf可以查看jar文件内容,jar文件大,包含内容多,dos看不全。 2:查看jdk中的rt.jar 文件 jar tvf rt.jar 3:jar tvf rt.jar>d:\rt.txt
5 集合部分
5.1 什么是集合?
存储对象的容器,集合中可以存储任意类型的对象,而且长度可变
5.2 集合和数组的区别
- 数组和集合类都是容器
- 数组长度是固定的,集合长度是可变的。
- 数组中可以存储基本数据类型,集合只能存储对象;
- 数组中存储数据类型是单一的,集合中可以存储任意类型的对象。
5.3 集合类的特点
用于存储对象,长度是可变的,可以存储不同类型的对象
5.4 集合的分类
5.4.1 集合类的继承实现关系
5.4.2 Collection
Collection描述的是集合公有的功能(CURD)
- Collection接口的共性方法
- 增加
add 将指定对象存储到容器中; add 方法的参数类型是Object 便于接收任意对象
addAll 将指定集合中的元素添加到调用该方法和集合中 - 删除
remove() 将指定的对象从集合中删除
removeAll() 将指定集合中的元素删除 - 修改
clear() 清空集合中的所有元素
- 判断
isEmpty() 判断集合是否为空
contains() 判断集合何中是否包含指定对象
containsAll() 判断集合中是否包含指定集合
equals()判断两个对象是否相等 - 获取
int size() 返回集合容器的大小
- 转成数组
toArray() 集合转换数组
- 增加
- List:有存储顺序,可重复
- List特有的方法
- 增加
void add(int index, E element) 指定位置添加元素
boolean addAll(int index, Collection c) 指定位置添加集合 - 删除
E remove(int index) 删除指定位置元素
- 修改
E set(int index, E element) 返回的是需要替换的集合中的元素
- 查找
E get(int index) 注意: IndexOutOfBoundsException
int indexOf(Object o) // 找不到返回-1
lastIndexOf(Object o) - 取子集合
List subList(int fromIndex, int toIndex) // 不包含toIndex
- 增加
- ArrayList(数组实现,查找快,增删慢)
- 原理
- 数组的内存空间地址是连续的。
- ArrayList底层维护了一个Object[] 用于存储对象,默认数组的长度是10。可以通过 new ArrayList(20)显式的指定用于存储对象的数组的长度。当默认的或者指定的容量不够存储对象的时候,容量自动增长为原来的容量的1.5倍。
- 由于ArrayList是数组实现, 在增和删的时候会牵扯到数组增容, 以及拷贝元素. 所以慢。数组是可以直接按索引查找, 所以查找时较快。
- 原理
- LinkedList(链表实现,增删快,查找慢)
- 原理
- 由于LinkedList在内存中的地址不连续,需要让上一个元素记住下一个元素.所以每个元素中保存的有下一个元素的位置.虽然也有角标,但是查找的时候,需要从头往下找,显然是没有数组查找快的。但是,链表在插入新元素的时候,只需要让前一个元素记住新元素,让新元素记住下一个元素就可以了.所以插入很快。
- 由于链表实现, 增加时只要让前一个元素记住自己就可以, 删除时让前一个元素记住后一个元素, 后一个元素记住前一个元素. 这样的增删效率较高。
- 但查询时需要一个一个的遍历, 所以效率较低。
- 特有的方法
- addFirst(E e)
- addLast(E e)
- getFirst()
- getLast()
- removeFirst()
- removeLast()
- element() 获取但不移除列表的头;如果集合中没有元素,获取或者删除元素抛出:NoSuchElementException
- 栈
- push()
- pop()
- 队列(双端队列)
- offer()
- poll()
- descendingIterator() 返回逆序的迭代器对象
- 原理
- ArrayList和LinkedList的存储查找的优缺点
- ArrayList 是采用动态数组来存储元素的,它允许直接用下标号来直接查找对应的元素。但是,但是插入元素要涉及数组元素移动及内存的操作。
总结:查找速度快,插入操作慢。
- LinkedList 是采用双向链表实现存储,按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快
- ArrayList 是采用动态数组来存储元素的,它允许直接用下标号来直接查找对应的元素。但是,但是插入元素要涉及数组元素移动及内存的操作。
- Set:无存储顺序,不可重复
- 对象的相等性:hashCode和equals
- equals为true==>hashCode相等,两个对象相等
- hashCode不相等==>equals为false,两个对象不相等
- hashCode相等==>equals不一定为true,两个不同的对象可能会有相同的hash值(哈希碰撞)
- ==和equals的区别
- ==是运算符,用于比较两个变量是否相等,基本类型用==,比较的是它们的值,对象用==,比较的是内存地址,对象类型不同返回false
- equals是进行逻辑比较,所以通常需要重写该方法来提供逻辑一致性的比较
- 对象的相等性:hashCode和equals
- HashSet
线程不安全,存取速度快,底层是以哈希表实现的
-
HashSet不存入重复元素:
元素的哈希值是通过元素的hashcode方法 来获取的, HashSet首先判断两个元素的哈希值,如果哈希值一样,接着会比较equals方法 如果 equls结果为true ,HashSet就视为同一个元素。如果equals 为false就不是同一个元素。
哈希值相同equals为false的元素是怎么存储呢,就是在同样的哈希值下顺延(可以认为哈希值相同的元素放在一个哈希桶中)。也就是哈希一样的存一列。
-
HashSet的add()方法的返回值是Boolean类型,元素重复则添加失败,返回false;元素不重复则添加成功,返回true。
-
- TreeSet
红-黑树的数据结构,默认对元素进行自然排序(String),如果在比较的时候两个对象返回值为0,那么元素重复。
- 红黑树
红黑树是一种特定类型的二叉树
红黑树的算法规则:左小右大
- TreeSet的排序规则
1)让存入的元素自定义比较规则
2)给TreeSet指定排序规则- 方式一:元素自身具备比较性: 需要元素实现Comparable接口,重写compareTo方法,也就是让元素自身具备比较性,这种方式叫做元素的自然排序也叫做默认排序。
- 方式二:容器具备比较性: 当元素自身不具备比较性,或者自身具备的比较性不是所需要的。那么此时可以让容器自身具备。需要定义一个类实现接口Comparator,重写compare方法,并将该接口的子类实例对象作为参数传递给TreeMap集合的构造方法。
- 注意: 当Comparable比较方式和Comparator比较方式同时存在时,以Comparator的比较方式为主;
在重写compareTo或者compare方法时,必须要明确比较的主要条件相等时要比较次要条件。(假设姓名和年龄一直的人为相同的人,如果想要对人按照年龄的大小来排序,如果年龄相同的人,需要如何处理?不能直接return 0,因为可能姓名不同(年龄相同姓名不同的人是不同的人)。此时就需要进行次要条件判断(需要判断姓名),只有姓名和年龄同时相等的才可以返回0.)
- 红黑树
- LinkedHashSet
会保存插入的顺序
- Vector:线程安全
- 比较
ArrayList: 单线程效率高;
Vector: 多线程安全的,所以效率低。 - 特有的方法
void addElement(E obj) 在集合末尾添加元素
E elementAt( int index) 返回指定角标的元素
Enumeration elements() 返回集合中的所有元素,封装到Enumeration对象中
- 比较
- Enumeration 接口:
boolean hasMoreElements() 测试此枚举是否包含更多的元素。
E nextElement() 如果此枚举对象至少还有一个可提供的元素,则返回此枚举的下一个元素。
5.4.3 迭代器
对 Collection 进行迭代的类,称其为迭代器
- Iterable
Collection的父接口. 实现了Iterable的类就是可迭代的.
- Iterator
Iterator iterator() 返回该集合的迭代器对象
- Iterator接口定义的方法
- boolean hasNext() 判断集合中是否有元素,如果有元素可以迭代,就返回true。
- E next() 返回迭代的下一个元素,注意: 如果没有下一个元素时,调用next元素会抛出NoSuchElementException
- void remove() 从迭代器指向的集合中移除迭代器返回的最后一个元素(可选操作)。
- 使用迭代器清空集合
Iterator it = coll.iterator(); while (it.hasNext()) { it.next(); it.remove(); } - 细节
- 如果迭代器的指针已经指向了集合的末尾,那么如果再调用next()会返回NoSuchElementException异常
- 如果调用remove之前没有调用next是不合法的,会抛出IllegalStateException
- 注意在对集合进行迭代过程中,不允许出现迭代器以外的对元素的操作,因为这样会产生安全隐患,java会抛出异常并发修改异常(ConcurrentModificationException),普通迭代器只支持在迭代过程中的删除动作。
- ConcurrentModificationException: 当一个集合在循环中即使用引用变量操作集合又使用迭代器操作集合对象, 会抛出该异常。
- List特有的迭代器【ListIterator】
- public interface ListIterator extends Iterator
- ListIterator listIterator()
- 方法
- add(E e) 将指定的元素插入列表(可选操作)。该元素直接插入到 next 返回的下一个元素的前面(如果有)
- void set(E o) 用指定元素替换 next 或 previous 返回的最后一个元素
- hasPrevious() 逆向遍历列表,列表迭代器有多个元素,则返回 true。
- previous() 返回列表中的前一个元素。
- 与Iterator对比
- Iterator在迭代时,只能对元素进行获取(next())和删除(remove())的操作。
- 对于 Iterator 的子接口ListIterator 在迭代list 集合时,还可以对元素进行添加(add(obj)),修改set(obj)的操作。
- Iterator接口定义的方法
5.4.4 Map
- Map和Collection的对比
- Map一次存一对元素, Collection 一次存一个。Map 的键不能重复,保证唯一。
- Map 一次存入一对元素,是以键值对的形式存在.键与值存在映射关系.一定要保证键的唯一性.
- HashMap
底层是哈希表数据结构,要保证键的唯一性,必须覆盖hashCode方法和equals方法
- HashTable
底层是哈希表数据结构
- HashMap和HashTable的区别
- 线程同步
- HashMap线程不同步
- HashTable线程同步
- null作为键和值
- HashMap可以存入null键、null值;
- HashTable不可以
- 继承实现
- HashMap继承AbstractMap,实现Map
- HashTable继承Dictionary,实现Map
- 遍历方式
HashTable可以用Enumeration - 哈希值的使用方式
- HashMap重新计算hash值,而且用&代替求模
- HashTable直接使用对象的hash值
- 内部实现的初始大小
- HashMap初始16,而且一定是2的倍数
- HashTable初始11,扩容:old*2+1
- 线程同步
- HashTable和ConcurrentHashMap的区别
- HashTable对整张表加锁
- ConcurrentHashMap将Hash表分为16个桶(segment),每次只对需要的桶加锁 (jdk1.8 时,对Node的节点加锁,大大提高了并发度)
- TreeMap
TreeMap可以对集合中的键进行排序,其实现与TreeSet一样
- 常用方法
- 添加
V put(K key, V value)(可以相同的key值,但是添加的value值会覆盖前面的,返回值是前一个,如果没有就返回null)
putAll(Map<? extends K,? extends V> m) 从指定映射中将所有映射关系复制到此映射中(可选操作)。 - 删除
remove() 删除关联对象,指定key对象
clear() 清空集合对象 - 获取
value get(key); 可以用于判断键是否存在的情况。当指定的键不存在的时候,返回的是null
int size() - 判断
- boolean isEmpty() 长度为0返回true否则false
- boolean containsKey(Object key) 判断集合中是否包含指定的key
- boolean containsValue(Object value) 判断集合中是否包含指定的value
- 添加
- 遍历Map的方式
- 1、将map 集合中所有的键取出存入set集合
Set keySet() 返回所有的key对象的Set集合(Iterator),再通过get方法获取键对应的值。
- 2、 values() ,获取所有的值.
Collection values()不能获取到key对象
- 3、 Map.Entry对象 推荐使用 重点
Set<Map.Entry<k,v>> entrySet()
- 将map 集合中的键值映射关系打包成一个Map.Entry对象
- 通过Map.Entry 对象的getKey,getValue获取其键和值。
- 1、将map 集合中所有的键取出存入set集合
5.4.5 Collections和Arrays
- Collections
Collections:集合框架中的工具类,特点:该工具类中的方法都是静态的。
- Collections常见方法
- 对list进行二分查找:
前提该集合一定要有序。
int binarySearch(list,key);//必须根据元素自然顺序对列表进行升级排序
int binarySearch(list,key,Comparator);//要求list 集合中的元素都是Comparable 的子类。 - 对list集合进行排序:
sort(list); //对list进行排序,其实使用的,是list容器中的对象的compareTo方法
sort(list,comaprator);//按照指定比较器进行排序 - 对集合取最大值或者最小值:
max(Collection)
max(Collection,comparator)
min(Collection)
min(Collection,comparator) - 对list集合进行反转:
reverse(list); - 对比较方式进行强行逆转
Comparator reverseOrder();
Comparator reverseOrder(Comparator); - 对list集合中的元素进行位置的置换
swap(list,x,y) - 对list集合进行元素的替换。如果被替换的元素不存在,那么原集合不变
replaceAll(list,old,new) - 可以将不同步的集合变成同步的集合
Set synchronizedSet(Set s)
Map synchronizedMap(Map<K,V> m)
List synchronizedList(List list) - 将集合变数组
toArray
- 对list进行二分查找:
- Collections常见方法
- Arrays
Arrays:用于对数组操作的工具类
- Arrays常见方法
- 二分查找,数组需要有序
binarySearch(int[])
binarySearch(double[]) - 数组排序
sort(int[])
sort(char[])
…… - 将数组变成字符串
toString(int[]) - 复制数组
copyOf(); - 复制部分数组
copyOfRange() - 比较两个数组是否相同
equals(int[],int[]) - 将数组变成集合
- List asList(T[])
- 这样可以通过集合的操作来操作数组中元素,但是不可以使用增删方法,add,remove。因为数组长度是固定的,会出现UnsupportOperationExcetion。
- 可以使用的方法:contains,indexOf ……
- 如果数组中存入的基本数据类型,那么asList会将数组实体作为集合中的元素。
- 如果数组中的存入的引用数据类型,那么asList会将数组中的元素作为集合中的元素。
- 如果数组中存入的基本数据类型,那么asList会将数组实体作为集合中的元素。
- List asList(T[])
- 二分查找,数组需要有序
- Arrays常见方法