第五章:Java高级特征
5.1两个关键字
5.1.1static
-
在成员变量声明时使用static,则该变量成为类变量或静态变量,不允许修饰局部变量
-
在加载该类时,只分配一次空间,并初始化,在该类所有实例之间是共享的
-
静态变量&实例变量
- 静态变量前要加static关键字,而实例变量前则不加
- 类的静态变量在内存中只有一个,java虚拟机在加载类的过程中为静态变量分配内存,静态变量位于方法区,被类的所有实例共享。静态变量可以直接通过类名进行访问,其生命周期取决于类的生命周期
- 实例变量取决于类的实例。每创建一个实例,java虚拟机就会为实例变量分配一次内存,实例变量位于堆区中,其生命周期取决于实例的生命周期
-
在类的成员方法声明中带有static关键字,则该方法就成为类方法和静态方法。可以直接被类调用,而不需要生成任何实例
public class GeneralFunction{
public static int addUp(int x, int y){
return x+y ;
}
}
public calss UseGeneral{
public void method(){
int c = GeneralFunction.addUp(9,10);
}
}
- 静态变量和静态方法
- 静态方法和静态变量是属于某一个类,而不属于类的对象
- 静态方法和静态变量的引用直接通过类名调用
- 可以用类的对象来调用静态的方法和访问静态变量
- 在静态方法中不能调用非静态的方法和引用非静态的成员变量, 反之,则可以
- 静态方法中不能使用this、super关键字
- 子类不能重写父类的静态方法,但在子类中可以声明与父类静态方法相同的方法(子类的静态方法被隐藏)
class Parent {
public static void add() {
System.out.println("Parent");}
}
class Children extends Parent {
public static void add() {
System.out.println("Children");}
}
public class TestStaticMethod {
public static void main(String[] args) {
Children ch = new Children(); //Children
// Parent ch = new Children(); //Parent 向上转型
//Children c = (children) ch;
//c.add(); //Children
ch.add();
}
}
- 没有存在于任何方法体中的静态语句块。在加载该类时执行且只执行一次
public Class StaticInitDemo{
static int i=5;
static {
System.out.println("Static code: i="+ i++);
}
}
public class Test {
public static void main(String args[]){
System.out.println(" Main code: i="+StaticInitDemo.i);
}
}
//运行结果
Static code: i=5
Main code: i = 6
5.1.2final
- 被定义成final的类不能有子类,即不能被继承
- 被定义成final的成员方法不能被重写,但可以被继承
- 把方法锁定,防止任何继承类修改它的意义和实现
- 高效。编译器在遇到调用final方法时候会转入内嵌机制,大大提高执行效率
- 被定义成final的成员变量不能改变。该变量实际上是常量,一般大写,并赋值
final int NUMBER = 100;
为了节省内存空间,我们常将常量声明为 static,final。如:
static final double PI=3.1415926; - final变量的赋值:
- 普通成员变量可默认初始化,但是final类型的成员变量必须显式地初始化
- 对于final类型的实例变量,可以在定义变量时初始化,或者在构造方法中初始化
- 对于final类型的静态变量,可以在定义变量时初始化,或者在静态代码块中初始化
public class FinalVarTest {
final int finalVar1=1; //声明final变量并显式初始化
final int finalVar2; //声明final变量
static final int finalVar3;//声明final类型的静态变量
public FinalVarTest(){
finalVar2=2; //构造方法中进行初始化
}
static {
finalVar3=1; //在静态代码块中进行初始化
}
}
- final方法会在编译的过程中利用内嵌机制进行inline优化;
inline优化是指:在编译的时候直接调用函数代码替换,而不是在运行时调用函数
5.2抽象类与接口
5.2.1抽象类
- 一个类如果只声明方法而没有方法的实现,则称为抽象类。
- 必须在声明中增加 abstract 关键字,在无方法体的方法前也要加上abstract
- 抽象类也可有普通的成员变量或方法
- 抽象类不能直接用来生成实例,一般可通过定义子类进行实例化
- 可以生成抽象类的变量,该变量可以指向具体的一个子类的实例
abstract class Employee{
abstract void raiseSalary(int i);
}
class Manager extends Employee{
void raiseSalary(int i ){ ….}
}
Employee e = new Manager( );
- 抽象类的特点:
- 抽象类不一定要包含抽象方法,若类中包含了抽象方法,则该类必须被定义为抽象类
- 如果一个非抽象类是某个抽象类的子类,它必须重写父类所有的抽象方法,子类对抽象方法的重写不能降低访问权限
- 抽象类不能被实例化 (不能用new创建对象), 必须被继承,抽象方法必须在子类中被重写,抽象类不用final修饰
- 抽象方法只需声明,无需实现;不能加final、private、static修饰符
- 构造方法、静态方法不能被声明为抽象方法
- 抽象类声明的对象可以成为其子类对象的上转型对象,并调用子类重写的方法
5.2.2接口
- interface 是在抽象类概念的基础上演变而来的
- 一个interface所有成员方法都是抽象的,默认具有public,abstract属性。在接口中定义的常量默认为具有public,static,final 属性,且必须给其初值,所以实现类中不能重新定义,也不能改变其值
- interface定义了一组行为的协议。两个对象之间通过这个协议进行通信
- interface 不属于类层次结构,不相关的类可以实现相同的接口
- 实现接口:用implements声明子类,该子类中必须实现接口(及其超类)中的所有方法
- 接口与多重继承:一个类可只继承一个父类,并实现多个接口,达到多重继承的目的
- 接口与多态:一个interface 可作为类名使用,实现多态
- 不能向interface定义中随意增加方法,需要通过继承扩展接口
- 类分组 package 语句:包(package)是相关类与接口的一个集合,它提供访问控制与命名空间管理。
Java平台中的类与接口都是根据功能以包组织的- 每个包都创建一个新的命名空间,因此不同包中的类名不会冲突
接口 | 抽象类 | |
---|---|---|
语法 | interface 接口名{ 方法();} | abstract class 抽象类名{ } |
实例化 | 不能直接实例化 | 不能直接实例化 |
方法 | 接口中的方法全部是抽象方法 | 抽象类中的方法不一定全部是抽象方法 |
继承 | 一个类可以实现多个接口 | 一个子类只能有一个直接父类 |
成员权限 | 接口中的成员都是public,一般缺省 | 抽象类中的成员不一定是public |
5.3泛型与集合类
5.3.1泛型
- 泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数
- 泛型的定义:使用“class 名称<泛型列表>”声明 一个类,为了和普通的类有所区别,这样声明的类称作泛型
public class MyBox<T>{
private T t;
public void add(T t){
this.t = t;
}
public T get(){
reture t;
}
}
- 泛型优点:
- 线程安全
- 消除强制类型转换
5.3.2集合类
- 一个集合类对象表示了一组对象,相当于一个容器
1.Set集合
- Set不能包含重复的元素
- Set判断两个对象相同不是使用==运算符,而是根据equals()方法
- 数组与集合区别:
- 数组是大小固定的,一旦创建无法扩容;集合大小不固定
- 数组的存放的类型只能是一种,集合存放的类型可以不是一种(不加泛型时添加的类型是Object)
- 数组是java语言中内置的数据类型,是线性排列的,执行效率或者类型检查,都是最快的。
集合操作有丰富的API
- set相关方法:
//基本操作
int size();
boolean isEmpty();
boolean contains(Object element);
boolean add(Object element);
boolean remove(Object element);
Iterator iterator();
//集合元素批操作
boolean addAll(Collection);//并集
boolean containsAll(Collection);
boolean removeAll(Collection);//差
boolean retainAll(Collection);//交集
//数组操作
Object[] toArray():返回一个包含容器中所有元素的数组
Object[] toArray(Object[] a):返回一个包含容器中所有元素的数组,且这个数组不是普通的Object数组,它的类型应该同参数数组a的类型相同(要做类型转换)
-
Set实现类:
-
HashSet:
-
HashSet集合是采用Hash表存储元素,所以当集合中的元素数量较大时,其访问效率要比线性列表快
-
HashSet集合中的对象是无序的,是根据对象的哈希码确定对象的存储位置
-
要存放到HashSet的各个对象重写hashCode()和equals()
-
Java规定:如果两个对象值相同,hashCode值一定要相同;如果两个对象的hashCode值相同,它们并不一定相同
-
//定义一个学生类,重写hashCode()和equals() class Student{ int no; String name; public Student(int no,String name){ this.no =no; this.name =name; } public int hashCode(){ //重写hashCode()方法 return no ; //学号即为哈希码 } public boolean equals(Object o){//重写equals()方法 Student s = (Student) o; return no == s.no && name.equals(s.name); } } Set<Integer> s = new HashSet<Integer>();//创建一个HashSet对象,缺省的初始容量是16,加载因子0.75
-
-
TreeSet:
-
采用一种有序树的结构(二叉树中序遍历)存储集合中的元素,TreeSet对象中元素按升序排序
-
实现方法:
-
类本身实现Comparable接口,实现compareTo()方法,小于返回负数,等于返回0,大于返回正数
class Student implements Comparable<Student> { private String no;// 学号 private String name;// 姓名 private int age;// 年龄 public Student() {} public Student(String no, String name, int age) { this.no = no; this.name = name; this.age = age; } @Override public String toString() { return "学号:" + this.no + ";姓名:" + this.name + ";年龄:" + this.age; } @Override public int compareTo(Student o) { return this.no.compareTo(o.no);// 学号升序 //int num = (this.age - o.age);//年龄升序 //1 return num; //为零,则两个对象值相等,只放一个,升序 //2 return num == 0 ? this.no.compareTo(o.no) : num;//年龄升序 }
-
定义另一类实现Comparator接口,实现compare()方法
class Student{ String no;// 学号 String name;// 姓名 int age;// 年龄 public Student() {} public Student(String no, String name, int age) { this.no = no; this.name = name; this.age = age; } @Override public String toString() { return "学号:" + this.no + ";姓名:" + this.name + ";年龄:" + this.age; } } class StudentComparator implements Comparator<Student>{ @Override public int compare(Student o1, Student o2) { // TODO Auto-generated method stub return o1.age-o2.age;//年龄升序,若两个对象值相等,只放一个 } }
接口 Comparable Comparator 排序逻辑 在待排序的类(对象)中实现 另定义一个类实现 排序方法 int compareTo(Object o1) int compare(Object o1,Objecto2) 所在包 java.lang.Comparable Java.util.Comparator 比较方式 内部比较器 外部比较器 耦合性 强耦合 松耦合
-
-
-
LinkedHashSet:
- LinkedHashSet则是继承了HashSet的子类,使用的是链表结构。也是根据hashCode值来决定元素存储位置,但它同时使用链表维护元素的次序,但是由于要维护元素的插入顺序,故性能略低于HashSet的性能
- 总结:如何选择HashSet和TreeSet,HashSet的性能比TreeSet好(特别是最常用的添加、查询元素等操作),因为TreeSet需要额外的算法来维护集合元素的大小顺序。只有当需要一个保持排序的Set时,才应该使用TreeSet,否则都应该使用HashSet。而HashSet的子类LinkedHashSet,对普通的插入、删除操作,LinkedHashSet比HashSet要稍微慢一点(维护链表开销),但是遍历会更快
-
2.List集合
- List是有序的集合,元素可以重复
- List接口定义:
public interface List extends Collection {
// 按位置存取元素
Object get(int index);
Object set(int index, Object element); // Optional
void add(int index, Object element); // Optional
Object remove(int index); // Optional
abstract boolean addAll(int index, Collection c); // Optional
…
}
-
List实现:
-
ArrayList:
-
ArrayList类采用可变大小的数组实现了List接口,ArrayList对象会随着元素的增加其容积自动扩大,是非同步的,是效率最高的也是最常用的
-
使用连续存储区域保存元素,它是一种直接访问结构,插入和删除操作会影响其他元素的位置
-
//创建 List list=new ArrayList<String>();//向上转型,提高程序的可扩展性 //一般方法 add(e);//增加元素e add(pos,e);//在位置pos增加元素e set(pos,e);//修改位置pos元素为e get(i);//取到位置pos元素 remove(e);//删除元素e remove(pos);//删除位置pos的元素 //遍历方法 Iterator<String> iterator = list.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next()); } for(String s:list){ System.out.println(s); } for(int x=0;x<list.size();x++){ System.out.println(list.get(x)); }
-
-
LinkedList:
-
LinkedList类采用链表结构实现List接口,可以用来实现堆栈、队列或双端队列,是非同步的
-
采用双向循环链表结构实现List接口
-
每个元素除了数据本身外,还有两个引用,分别指向前一个元素和后一个元素,顺序访问结构
-
插入和删除操作只需修改引用信息,执行效率高
-
//创建 LinkedList<String> strings = new LinkedList<>(); //特有方法 addFirst(Object obj);//在集合第一位添加一个元素 addLast(Object obj)//在集合最后一位添加一个元素 getFirst();//得到集合第一位的元素 getLast();//得到集合的最后一位元素 removeFirst();//删除集合的第一个元素 removeLast();//删除集合的最后一个元素
-
-
Vector:
- Vector类采用可变大小的数组实现了List接口,是同步的
- 总结:
- 如果需要遍历List集合元素,ArrayList、Vector集合,则应该使用随机访问方法(get)来遍历集合元素,这样性能更好。对应LinkedList集合,则应采用迭代器(Iterator)来遍历集合元素
- 如果需要经常执行插入、删除操作来改变List集合大小,则应该使用LinkedList集合
- 如果多条线程需要同时访问List集合中的元素,可以考虑使用Vector这个同步实现
-
3.Map集合
- Map把键值映射到某个值,一个键值最多只能映射一个值
- Map接口定义:
public interface Map {
Object put(Object key, Object value);
Object get(Object key);
Object remove(Object key);
boolean containsKey(Object key);
boolean containsValue(Object value);
int size();
boolean isEmpty();
}
-
Map实现:
-
HashMap:
-
采用Hash表实现Map,是无序的,HashMap是非同步的并允许有空的键与值(null)
-
//创建 Map<String, Integer> hashMap = new HashMap<>(); //常用方法 put();往集合中添加元素,key 值不可重复,重复时会覆盖之前的 value 值 size();返回集合长度 get(key);获取对应 key 值的 value 值 clear();清除集合中的所有元素 values();返回一个新集合,获取集合中所有元素的 values keySet();返回一个新集合,获取集合中所有元素的 key remove();根据 key 或者 key-value 去除集合中元素,并分别返回 value 值和 Boolean 值 iterator();返回一个迭代器对象 entrySet();将 Map 集合每个 key-value 转换为一个 Entry 对象,并返回由所有的 Entry 对象组成的 Set 集合 containsKey();判断集合中是否含右指定的 key 值 //遍历方法 //通过 keySet 遍历,普遍使用 for (String key : map.keySet()) { System.out.println("key= "+ key + " and value= " + map.get(key)); } //通过 entrySet 和迭代器遍历 Iterator<Map.Entry<String, Integer>> it = map.entrySet().iterator(); while (it.hasNext()) { Map.Entry<String, Integer> entry = it.next(); System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue()); }
-
-
HashTable
- 采用Hash表实现Map,是无序的
-
TreeMap
- 有序树的结构实现了Map的子接口SortedMap,该类将键的升序的次序排列元素
-
5.4枚举和包装类
- 集合操作类Collections:
public static void sort(List list); // 自然排序,list 必须实现 Comparable 接口
public static void reverse(List list);// 按照自然排序,进行反转,即降序
public static void shuffle(List list); //混排,与 sort 相反,完全打乱排列顺序
public static void copy(List dest, list src);// 复制,如果 dest 更长,则其剩余元素不受影响
- 集合的遍历
- 集合迭代器:是一个访问集合中元素的对象,起始位置处于第一个元素之前
- 两种迭代器接口:
- Iterator:完成迭代器基本功能所需的方法
- ListIterator:完成迭代器附加功能所需的方法(双向遍历,修改元素)
- Map集合的遍历
Set set =map.keySet();
Iterator it=set.iterator();
while(it.hasNext()){
String s= (String) it.next();
System.out.println(map.get(s));
}
-
枚举类型
-
一般用于表示一组常量,一个枚举声明实际上定义了一个类
-
[public] enum 枚举类型名 [implements 接口名表] { 枚举常量定义 [枚举体定义] } public enum Names { Li, Zhang, Wang, Zhao,Chen } Names name = Names.Wang;
-
-
包装类
-
Wrapper将基本类型表示成类
-
每个wrapper类对象都封装了基本类型的一个值
-
Primitive Data Type Wrapper Class boolean Boolean byte Byte char Character short Short int Integer long Long float Float double Double -
装箱:把基本类型的数据通过相应Wrapper类形成对象
-
拆箱:把一个Wrapper类对象中的基本数据类型提取出来
-
-
String类型:
-
String不是基本的数据类型,是类类型(引用类型)
-
String是不可变对象,常用的相关类StringBuilder (线程不安全,执行速度快), StringBuffer(线程安全,执行速度慢)
-
String和Integer相互转换:
-
// String To int int i = Integer.parseInt(“100”); int i = Integer.valueOf (“100”); // int To String String s = String.valueOf(100); String s = Integer.toString(100);
-
-
public static void main(String[] args) { String s1="ab"; String s2="a"+"b"; String s3=new String("ab"); String s4="ab"+"c"; String s5=s1+"c"; System.out.println(s1==s2);//true System.out.println(s1==s3);//false System.out.println(s4==s5);//false //“==”判断两个对象是否指向同一个引用,即是否为同一个对象 //equals方法比较两个字符串的内容是否相等 }
-