第四周
25 instanceof关键字
用于判断,左侧的引用是否属于右侧的类型
用于避免程序中引用数据类型转换异常
26 abstract 抽象类
1:抽象方法没有方法体,使用abstract关键字修饰,必须存在于抽象类中
abstract void fun();
//抽象方法必须为public 或者 protected 默认为public
当一个类包含了抽象方法,那么这个类就是一个抽象类,必须用abstract修饰
在《JAVA编程思想》一书中,将抽象类定义为“包含抽象方法的类”
抽象类就是为了继承而存在的
2:抽象类不能new对象,必须通过new子类的方式创建对象(多态向上转型的方式)
3:子类必须重写父类所有的抽象方法,除非子类也是抽象类
4:抽象类中可以书写普通属性,普通方法,构造方法,用于给子类使用
5:抽象类依然可以实现多态,实现的方式与之前一致:父类作为形参和父类作为返回值
6:静态方法可以被继承,不能被重写
27 final关键字
27.1修饰属性
final:最终的
适用场景,可以用来修饰:
属性:
被final修饰的属性,称之为常量,常量要求全部大写,多个单词之间使用下划线分割
常量只能被赋值一次,常量通常(99%)在定义的时候赋值,或者在构造方法中赋值
这两种方法都是为了保证,在使用常量之前是有值的
publci class Test{
final double PI=3.14;
}
public class Test1{
final double PI;
public Test1(){
PI=3.14;
}
public Test1(int i){
PI=3.14;
}
}
以上代码的弊端是,每创建一个Test/Test1对象,都会在内存中定义一个PI,没必要存在多份。
所以可以加上static关键字修饰
此时常量只能被赋值一次,常量通常(99%)在定义的时候赋值。
public class Test{
static final int A;
static {
A=1;
}
}
27.2 修饰方法
被final修饰的方法不能被重写
27.3 修饰类
被final修饰的类不能被继承(extends)
28 接口 interface
28.1 特点
1:接口中的方法默认都是全局抽象方法,写不写都有public abstract修饰
2:接口不能直接new对象,必须通过new实现类(子类)的方式来创建(向上转型)
3:实现类(子类)必须重写接口中的所有方法(抽象方法),
除非子类也是抽象类或者接口
4:接口中
不能写:
普通属性(默认都是(并且只能是)全局静态变量,public static final),
普通方法(默认都是(也只能是)public abstract),方法不能有具体实现,没有方法体,也就是抽象方法,
构造方法
5:接口可以继承多个接口
6:实现类可以实现多个接口
允许一个类遵循多个接口
如果一个非抽象类遵循了接口,就必须实现接口中所有的方法
抽象类遵循了接口,可以不实现接口中的抽象方法
7:接口依然可以实现多态,实现方式与之前一致:接口(父类)作为形参和返回值
implements:
implements是一个实现接口用的关键字,用来实现接口中定义的抽象方法,要用到某个接口时,先继承,再重写接口方法实现该功能
implements,实现父类,子类不可以覆盖父类的方法或者变量。即使子类定义与父类相同的变量或者函数,也会被父类取代掉
//例如
public interface People{
int a=1;
public say();//public abstract
}
//但是接口没有方法体,只能通过具体的类去实现其中的方法体、
public class Chinese implements people{
publci say(){
sout("你好");
}
}
28.2 接口和抽象类的区别
1:抽象类可以提供成员方法的实现细节,而接口中只能存在public abstract 方法;
2:抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的;
3:接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法;
4:一个类只能继承一个抽象类,而一个类却可以实现多个接口
抽象类是对事物的抽象,即对类抽象。包括属性行为
接口是对行为的抽象
当你关注事物的本质,使用抽象类
当你关注某个功能,使用接口
29 常用类
29.1 枚举
枚举可以用于规范属性的值,使用枚举声明类
public enum Week{
MON,TUE,WED,THUR,FRI,SAT,SUN
}
public class{
//使用Week.调用,
main(){
Week A=Week.MON;
}
//也可以作为参数传入
public void Flie(Week A){
A.
}
}
29.2 包装类
29.2.1 概念
每个基本数据类型 都有一个对应的包装类
byte short int long float double boolean char
Byte Short Integer Long Float Double Boolean Character
所有包装类都可将与之对应的基本数据类型作为参数,来构造他们的实例
除character类外,其他包装类可将一个字符串作为参数构造他们的实例
main{
Byte b1=new Byte((byte)20);
Byte b2 =20;
Byte b3=new Byte(b2);
Byte b1=new Byte("12");
//数字默认都是int,要转为Byte首先要是byte类型,要手动下降
//Boolean类 不区分大小写 除了true其他默认都是false
Boolean bl1 = new Boolean(true);
Boolean bl2 = new Boolean("tRuE");
Boolean bl3 = new Boolean("abc");
Character ch1 = new Character('A');
}
29.2.2 包装类转换为基本数据类型
xxxValue()将包装类转换为基本数据类型
每一个包装类提供了这样一个方法
Boolean类构造方法参数为String类型时,若该字符串内容为true(不分大小写),则该Boolean对象表示true,否则表示为false
当Number包装类构造方法参数为String时,字符串不能为null,且该字符串必须可解析为相应的基本数据类型,否则不通过,运行时会抛出
NumberFormatException异常
Byte b1 = new Byte("120");
byte b = b1.byteValue(); // shift + alt + L 生成对应的变量接收方法返回值
System.out.println(b);
29.2.3 基本类型转为包装类
valueOf() 每个包装类都提供valueOf方法,用于将基本数据类型转换为包装类型
Integer i1 = Integer.valueOf(200);
Integer i2 = Integer.valueOf("230");
29.2.4 基本类型转为字符串
toString():以字符串形式返回包装对象表示的基本数据类型(基本数据类型->字符串)
String str1 = Byte.toString((byte)20);
String str5 = Float.toString(3.14F);
String str7 = Boolean.toString(false);
String str8 = Character.toString('A');
29.2.5字符串转为基本数据类型
parseXXX() :把字符串转为相应的基本数据类型(character除外) 字符串->基本类型
byte b1 = Byte.parseByte("23");
int i1 = Integer.parseInt("200");
29.2.6 包装类面试题
Short Integer Long Character 包装类使用=赋值
且值的范围在-128-127时不会产生新对象,==比较为true
超出范围会new新的对象,==为true
Integer i1=127;
Integer i2=127;
sout(i1==i2)//true
Integer i3=128;
Integer i4=128;
sout(i3==i4)//false
29.3 自动装箱和拆箱
从JDK1.5开始,允许包装类和基本数据类型混合使用 减少代码量
装箱:将基本数据类型自动转换为包装类型
拆箱:将包装类型自动转换为基本数据类型
//装箱
Integer i1=20;
//拆箱
int i2=i1;
29.4 Math类
Math类,数学工具类,提供常用的数学计算方法和两个静态常量E PI
Math.E
Math.PI
Math.ceil(double a) 向上取整
Math.floor(double a) 向下取整
Math.round(double a) 四舍五入
Math.random() 随机数,0~1的小数
0~n
返回到多少的随机数n,就(Math.random() * n
29.5Random类
随机数类,丰富的方法
没有Short和byte
Random ran1 = new Random();
System.out.println(ran1.nextBoolean());
System.out.println(ran1.nextDouble());
System.out.println(ran1.nextFloat());
System.out.println(ran1.nextLong());
System.out.println(ran1.nextInt());
System.out.println(ran1.nextInt(55));
//只有int可以指定范围
Random ran2 = new Random(11);
Random ran3 = new Random(11);
//11是种子,种子相同,得到的随机数相同
//当同时有double 和float时,float由double决定
29.6 String类
length() :长度
equals():比较内容
equalsIgnoreCase():忽略大小写比较
toLowerCase():转换为小写
toUpperCase():转换为大写
concat():拼接字符串
indexOf():搜索第一个出现的字符ch(或字符串value),如果没有找到,返回-1
lastIndexOf():搜索最后一个出现的字符ch(或字符串value),如果没有找到,返回-1
substring(int index)提取从位置索引开始的字符串部分
substring(int beginindex, int endindex)提取beginindex和endindex之间的字符串部分 取头不取尾
trim()返回一个前后不含任何空格的调用字符串的副本
split()拆分字符串
endsWith() 判断字符串是否以某一个字符串结尾
startsWith()判断字符串是否以某一个字符串开头
replace()替换字符串
29.6.1 String类底层实现
String 类底层维护的是一个char数组
private final char value[];
并且这个数组是用final修饰,不可更改,所以String类的对象是不可变的
也就是说,字符串内容一旦确定是不可能发生改变的,任何对字符串改变的操作都将产生新的字符串
String a=“a”;
a+=“b”;
此时将产生三个字符数组,分别存储 a b ab
我们发现,不能对一个字符串内容进行频繁的改动,因为这样会产生很多的字符串对象,这些对象将占用内存空间
29.6.2 StringBuffer和StringBuilder的区别
这个都是可变的字符序列
private transient char[] toStringCache;
区别:
这两个依然属于字符串对象,只不过内容是可变的
StringBuffer 是线程安全的,多线程的,源自JDK1.0
StringBuilder 线程不安全,源自JDK1.5
29.6.3 为什么不能用==比较String对象
String a=“abc”;
String b=“abc”;
此时用==比较为true,按理说String作为引用数据类型比较的是地址,应该为false
但是String直接用=赋值的话,字符串对象将存在于常量池中。
第二次如果赋值相同的内容,会先去常量池中查找是否有相同的内容。
如果有,则将之前的地址重新赋值给当前变量,所以使用==比较为true
如果没有,则将当前内容存放在常量池中,等待下一次赋值。
String a=new String(“a”);
而用new关键字创建字符串对象,每一次都将开辟新的空间,所以使用==比较为false,而要用equals()比较,因为equals比较时转化为字符数组逐个比较。
30 集合
30.1 Collection接口
30.2 List接口
30.2.1 ArrayList
//定义:
//普通定义,不规定集合中的数据类型
ArrayList list1=new ArrayList();
//泛型定义,规定集合中的数据类型
ArrayList<包装类型> list2=new 、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、ArrayList<包装类型>();
//常用方法:
ArrayList<Integer> list=new ArrayList<Integer>();
//add 增
list.add(数值);
//remove 删 位置
list.remove(位置)
//set 改
list.set(位置,值)
//get 查
list.get(位置)
//size() 长度
list.size();
三种遍历
ArrayList<Integer> list=new ArrayList<Integer>();
//普通for循环遍历
for(int i=0;i<list.size();i++){
sout(list.get(i));
}
//迭代器遍历
Iterator<Integer> it1=list.iterator();
while(it1.hasNext()){
sout(it1.next());
}
//增强for循环,底层实现依然是迭代器,是JDK1.5的新写法,依然是基于迭代器实现的
for( Integer i : list){
sout(i);
}
底层实现
ArrayList 的特点:背背背
1有序,可以重复,可以为null,线程不安全
2底层实现是一个Object类型的数组
当我们调用无参构造方法,底层维护一个空数组,当我们第一次添加元素,将数组长度改为10
扩容为原数组的1.5倍
3查询,修改快,因为有下标
增加慢,因为需要扩容
删除慢,因为需要移动元素(数组)
30.2.2 Vector
常用方法和遍历和ArrayList相同
Vector和ArrayList的区别:
1 Vector是线程安全的,JDK 1.0
ArrayList线程不安全,JDK1.2
2 Vector 调用无参构造直接初始化一个长度为10的数组
ArrayList调用无参构造初始化一个空数组
3 Vector扩容2倍
ArrayList扩容1.5倍
30.2.3 LinkedList
常用方法:
LinkedList基于双向链表实现的集合
除了与ArrayList相同的增删改查方法外
还单独提供了用于操作头部和尾部的方法
操作方法:
addFirst(); 添加到头
getFirst(); 获得第一个
addLast(); 添加到尾
getLast(); 获得最后一个
removeFirst(); 删除第一个
removeLast(); 删除最后一个
isEmpty(); 判空
底层实现
LinkedList的特点:
1 有序的,允许重复的,允许为null,线程不安全
2 根据下标查询,修改慢,因为不能直接查询到摸个元素,必须先查询相邻元素
3 增加和删除快,因为不需要扩容,不需要移动元素
get()方法优化:当我们根据下标查询LinkedList元素的时候
先对下标进行判断,如果下标是小于集合总长度中间值,从前往后查询
否则,从后往前找
对于LinkedLIst的三种遍历的速度
普通for(29秒)>>迭代器(0.9秒)=增强for(0.9秒)
鉴于LinkedList数据结构特点,不要使用普通的for循环遍历
30.3 Map接口
30.3.1 HashMap
由键值对组成,一一对应
常用方法
HashMap<String,String> map=new HashMap<String,String>();
//put(键,值);
map.put("CN","中国");
//get(键) 获取所给键的值
map.get("CN");
//remove(键) 有返回值
map.remove("CN");
//replace(旧键,新值) 改变所给键的值
map.replace("CN","中华民国");
//map.size(); 长度
//map.clear(); 清空
//map.isEmpty();判空
六种遍历
HashMap<String,String> map=new HashMap<String,String>();
//第一种,获取所有键
Set<String> keySet=map.keySet();
for(String key : KeySet){
sout(key+"="+map.get(key));
}
//第二种,获取所有值
Collection<String> values=map.values();
for(String v : values){
sout(v);
}
//第三种,获取所有键和值的组合
Set<Entry<String,String>> entrySet = map.entrySet();
for(Entry<String,String> entry : entrySet){
sout(entry.getKey()+"="+entry.getValue());
}
//第四种,获取所有键的迭代器
Iterator<String> it1=map.keySet().iterator();
while(it1.hasNext()){
String key=it1.next();
sout(key+"="+map.get(key));
}
//第五种,获取所有值得迭代器
Iterator<Stirng> it2=map.values().iterator();
while(it2,hasNext()){
sout(it2,next());
}
//第六种,获取所有键和值的组合的迭代器
Iterator<Entry<String,String>> it3=map.entrySet().iterator();
while(it3.hasNext()){
//1
Entry<String,String> entry=it3.next();
sout(entry.getKey()+"="+entry.getValue());
//2
sout(it3.next());
}
数据结构
HashMap数据结构
JDK1.7 数组+单向链表
JDK1.8 数组+单向链表+红黑树(二叉排序树,左小右大)
HashMap存放数据过程:根据key所计算出来的hash值,决定当前元素在数组的位置,如果当前位置没有元素,则直接存放。
如果当前位置有元素,则向下延伸为单向链表,如果单向链表的长度超过8,将转换为红黑树
后序链表以后的元素减少到6以下,再将红黑树转换为单向链表。
扩容当数组的使用率达到75%,并且集合中的元素个数大于64,扩容两倍
30.3.2 Hashtable
HashMap与Hashtable的区别?
HashMap线程不安全,初始长度16,扩容2倍
Hashtable线程安全,初始数组长度11,扩容2倍+1
30.4 泛型
泛型:用于规范集合,或者接口,类中的数据结构
泛型书写位置:
类
接口上
形参
返回值
泛型通用的字母:
T Type 类型
E Element 元素
R Return 返回值
P Parameter 参数
K Key 键
V Value 值
以下代码目前供了解