1、修饰符 static
1)静态类变量(修饰成员变量):
1.Static int data 语句说明 data 为类变量,为一个类的共享变量,是所有对象共享的,它不属于任何对象,是属于整个类的(静 态方法也是一样)。
2.Static 定义的是一块为整个类共有的一块存储区域。
3.其变量可以通过类名去访问:类名.变量名(与通过对象引用访问变量是等价的)。
2)静态方法(修饰方法):
1.Public static void printData(){}:表明此类方法为类方法(静态方法),访问是在编译期完成,执行效率比较高。
2.静态方法不需要有对象,可以使用类名调用(不需要实例也可以调用静态方法)。
3.静态方法中不能访问类的非静态成员,包括成员变量和方法;只能访问本类中的静态变量和其它静态方法。因为此时是通过类 调用的,没有对象的概念。方法中 this.data 和super.data 是不可用的。
原因:从根本上说,静态变量不管类是否实例化都会存在,而实例变量只有类实例化了才存在。直接调用静态方法时并不确定实例变量是否存在。
4. 一般情况下,主方法是静态方法,所以 JVM 可以直接调用它,主方法为静态方法是因为它是整个软件系统的入口,而进入入 口时系统中没有任何对象,只能使用类调用。
5.静态方法不能被覆盖,如果子类中有和父类重名的静态方法,虽然编译通过,但它并不能实现多态,所以不能称作覆盖。例 如:
class Super{
static public void show(){System.out.println("in Super");}
}
class Sub extends Super{
static public void show(){System.out.println("in Sub");}
}
public class Test {
public static void main(String[] arg) {
Super s = new Sub();
s.show();
}
}
执行结果是: in Super。
3)静态代码块(修饰没有名字的代码块):
1.只被执行一次;
2.初始化块在类被加载后首先被运行,不管类是否实例化,而且只执行这一次
3.作用:一般用来初始化一些复杂类型的静态变量。
4)静态内部类(注意:只能修饰成员内部类):
class Out{
public static class Inner{}
}
5)Static 通常用于 Singleton 模式开发:
Singleton 模式(单例模式):是一种设计模式,高于语法,可以保证一个类在整个系统中仅有一个对象。
1.问题域:
系统中你需要获得某个类的唯一实例,所有客户端对它的访问都将通过一个公共的访问点获得。
2.解决方案:创建一个类并使其
a.定义一个私有的构造器;
b.定义一个私有、静态的实例变量指向自己(类型是自己的类型);
c.定义一个公有、静态的访问方法 getInstance()用于返回该类的唯一实例(注:懒汉式需同步)。
3.分类:
a.饿汉式:在类装载的时候就进行实例化;
b.懒汉式:在公有、静态的访问方法(同步:用 synchronized 修饰)中进行实例化,用的多一些。
// 饿汉式实现
public class ConnectionFactory{
private static Connection conn;
static{
conn = new Connection();
}
public static Connection getInstance(){
return conn;
}
}
// 没有解决同步的懒汉式:
public class ConnectionFactory{
private static Connection conn;
private Connection(){
if(conn==null)
conn = new Connction();
}
public Connection getInstance(){
return conn;
}
}
// 解决同步的懒汉式:
public class ConnectionFactory{
private static Connection conn;
private Connection(){
}
//同步函数
public static synchronized ConnectionFactory getInstance() {
if (conn == null)
conn = new ConnectionFactory();
return conn;
}
//同步加锁
public static ConnectionFactory getInStanceBlock(){
if(conn == null)
synchronized (ConnectionFactory.class) {
if(conn == null)
conn = new ConnectionFactory();
}
return conn;
}
}
2、修饰符 final
1)final 型变量(修饰变量时):
1. 当利用 final 修饰一个变量(属性)的时候,此时该变量变为常量。
注意:a. JAVA 命名规范中常量全部字母要求大写:Final int AGE=10;
b. 常量的地址不可改变,但在地址中保存的值(即对象的属性)是可以改变的。
2. 在 JAVA 中利用 public static final 的组合方式对常量进行标识(固定格式)。
3. Final 变量是在整个类被创建时候被赋值,之后就不能改变了。
注意:a. 对于 final 变量,如果在声明和构造的时候均不进行赋值,将显示编译出错。
b. 对于利用构造器对 final 变量进行赋值的时候,此时在构造之前系统设置的默认值被覆盖。
4. 常量赋值(这里的常量指的是实例常量,即成员变量):
① 在初始化的时候通过显式声明赋值:Final int x=3;
② 在构造的时候赋值。
2)final 型方法(修饰方法时):
1. final 方法不能被覆盖(不能被改写),只能被继承。
2. 为了保证方法的一致性(即不被改变),可将方法用 final 定义。
注意:a. 如果在父类中有 final 定义的方法,那么在子类中继承同一个方法。
b. 如果一个方法前有修饰词 private 或 static,则系统会自动在前面加上 final, private 和 static 方法默认均为 final 法。
c. final 并不涉及继承,继承取决于类的修饰符是否为private、default、protected 还是 public。也就是说,是否继承取 决于这个类对于子类的可见性。
3. Final 和 abstract 永远不会同时出现。
3)final 类(修饰类时):
1.final 类不能被继承,即 final 类没有子类。
2.可以用 final 保证用户调用时动作的一致性,可以防止子类覆盖情况的发生。
3.final 修饰类时表明同时类中的方法都是 final 型,但其变量不是。
3、修饰符 Abstract
1)抽象方法(修饰方法时):子类继承抽象类时必须实现其中的抽象方法
1.当 abstract 用于修饰方法时,此时该方法为抽象方法,此时方法不需要实现,实现留给子类覆盖,子类覆盖该方法之后方法 才能够生效。
2.抽象方法没有方法体,要求子类必须提供这个方法的实现。注意比较:private void print(){};此语句表示方法的空实现。
Abstract void print(); 此语句表示方法的抽象,无实现。
2)抽象类(修饰类时):
1. 如果将一个类声明为 abstract,表明此类不能创建对象(不能实例化),只能声明变量。
2. Abstract 类的设计是将子类的共性最大限度的抽取出来,以提高程序的统一性。
3. 带有抽象方法的类一定是抽象类,但抽象类可以没有抽象方法。
4. Abstract 类可以作为编译时类型,但不能作为运行时类型。
4、接口
1) 接口是抽象类的另外一种形式(没有实例变量的抽象类);
2) 在一个接口中所有方法都是抽象方法;
3) 接口中所有变量都必须被定义为 final static;
4) 接口可以继承多个接口(可多重继承);
5) 接口只有两种语法成分:静态常量和公有的抽象方法。
注:接口中的方法自动被置为 public, 因此在接口中声明方法并不需要提供 public 关键字。但在实现接口时,必须把方法声明 为public。
public interface IA{
public abstract void method();
void method2();
}
public interface IB{
void method3();
}
public interface IC extends IA,IB{
void method4();
}
public class C implements IC{} // implements 表示对接口 IC 中方法的实现,共有 4 个
5、Object 类
JAVA 中有一个特殊的类,它是 JAVA 体系中所有类的父类(直接父类或者间接父类),此类中的方法可以使所有的类均能继承。以下介绍的三种属于 Object 类的方法:
1)操作符“==”和方法 equals():
1.==:比较的是引用,表示其引用值是否相等,即是否指向同一对象;
注:“==”在任何时候都是比较地址,这种比较永远不会被覆盖。
2.equals: 比较的是对象,其默认比较规则就是"==";
注意:基本类型没有 equals(),只有复杂类型才有。
3.附加知识:字符串类为 JAVA 中的特殊类(String 类在 Java 中为 final 类型),一个字符串的值不可重复。因此在 JVM(虚 拟机)中有一个字符串池,专门用来存储字符串。如果遇到 String a="hello"时(注意没有 new,不是创建新串),系统在 字符串池中寻找是否有"hello",此时若字符串池中没有"hello",那么系统将此字符串存到字符串池中,然后将"hello"在字符 串池中的地址返回给 a。如果系统再遇到 String b="hello",此时系统在字符串池中可以找到"hello"字符串,并将其地址返回 给 b,则此时 a 与 b 相同。
4.举例:
a. String a = new String("hello");
String b = new String("hello");
System.out.println(a==b);
则 a==b 的返回为结果为 false。 //因为==判断的是引用是否相等,并不是判断对象。
b. String a = "hello";
String b = "hello";
System.out.println(a==b);
系统的返回值为 true。
c. 故如果要比较两个字符串是否相同(而不是比较它们的地址是否相同),可以对 a 调用 equals:
System.out.println(a.equals(b));
系统的返回值为 true。注意 equals()用来比较两个对象中字符串的顺序,即 a.equals(b)比较的是 a 与 b 的值。
5.注意下面程序:
student a=new student(“LUCY”,20);
student b=new student(“LUCY”,20);
System.out.println(a==b);
System.out.println(a.equals(b));
此时返回的结果为 false。因为 Student 继承的是 Object 的 equals()方法,此时toString()等于“==” ,为了实现对象的比较需 要覆盖 equals()方法(加上这个定义,则返回 ture或 false)。
6.实现标准 equals()方法的流程:
public boolean equals(Object o){
if (this==o) return true; //此时两者相同
if (o==null) return false;
if (! o instanceof Student) return false; //不同类
Student s = (Student)o; //强制转换
if (s.name.equals(this.name)&&s.age==this.age) return true;
else return false;
}
2)finalize 方法:当一个对象被垃圾收集器回收的时候调用的方法。
3)toString():是利用字符串来表示对象的方法,简化输出。
1.当我们直接打印定义的对象的时候,隐含的是打印 toString()的返回值(返回一个字符串)。
2.可以通过子类作为一个 toString()来覆盖父类的 toString()以取得我们想得到的表现形式,即当我们想利用一个自定义的方式 描述对象的时候,我们应该覆盖 toString()。
6、内部类(Inner class)
1)概念:内部类是指在一个类的内部再定义一个类。它可以访问外围类的私有成员,其语法特别繁琐,在实际开发中避免使 用。
注意:所有使用内部类的地方都可以不用内部类,但使用内部类可以使程序更加的简洁,便于划分层次结构和命名规范。
2)特点:1. 缩小类的访问范围,比包还小;
2. 用内部类定义在外部类中不可访问的属性,这样就在外部类中实现了比外部类的 private 还要小的访问权限。
注意:内部类是一个编译时的概念,一旦编译成功,就会成为完全不同的两个类。对于一个名为 outer 的外部类和一个在其 内部定义的名为 inner 的内部类,编译完成后就会出现outer.class 和 outer$inner.class 两个不同的类。
3)内部类可为静态,也可用 PROTECTED 和 PRIVATE 修饰(而外部类不可以,顶级类只能使用 PUBLIC 和 DEFAULT 修饰)。
注意:JAVA 文件中没有任何一个 public class 可以类名和文件名不同,但内部类可以。
4)内部类的分类:与成员变量同一级别(成员内部类和局部内部类);与方法中的局部变量同一级别(静态内部类和匿名内部 类(经常用到,必须掌握))。
5)成员内部类:
1.概念:作为外部类的一个成员存在,与外部类的属性、方法并列。是最复杂,用的最少的内部类。
2.特点:必须依附于外围类的实例而存在,能访问外围类的一切成员,成员内部类不能有静态属性。
3.在内部类中访问实例变量:this.属性在内部类访问外部类中与它命名冲突的成员:Outer.this.member在外部类的外部访问内 部类,使用 out.inner
4.构建成员内部类实例:
a.在外围类以外的地方:(new outer()).new Inner();
b.在外围类内部:this.new Inner();
6)静态内部类:
1.静态内部类定义在类中,任何方法外,用 static 定义。是最简单的内部类,与外围类的名字不能相同。
2.静态内部类只能访问外部类的静态成员。
3.不需要构建外围类实例就可直接构建静态内部类的实例:这是静态内部类和成员内部类的区别,静态内部类的对象可以直 接生成,而不需要通过生成外部类对象来生成。这样实际上使静态内部类成为了一个顶级类。静态内部类不可用 private 来 进行定义。
Outer.Inner in = new Outer.Inner();
4.注意: a.当类与接口(或者是接口与接口)发生方法命名冲突的时候,此时必须使用内部类来实现。
b.用接口不能完全地实现多继承,用接口配合内部类才能实现真正的多继承。
例子:对于两个类,拥有相同的方法,另有一个 robot 类:class Robot extends Person implement Machine{}。此时 run() 不可直接实现。
interface Machine{
void run();
}
class Person{
void run(){
System.out.println("run");
}
}
class Robot extends Person{
private class MachineHeart implements Machine{
public void run(){
System.out.println("heart run");
}
}
public void run(){
System.out.println("Robot run");
}
Machine getMachine(){
return new MachineHeart();
}
}
class Test{
public static void main(String[] args){
Robot robot = new Robot();
Machine m = robot.getMachine();
m.run();
robot.run();
}
}
6)局部内部类:
1.概念:在方法中定义的内部类称为局部内部类。
2.与局部变量类似,在局部内部类前不能使用修饰符 public,protected,private 和 static,其范围为定义它的代码块。
3.局部内部类可以访问外部类的一切成员,还可以访问这个方法中由 final 修饰的局部变量(即常量)。
4.可见范围缩小到一个方法的内部:在类外不可直接访问局部内部类(保证局部内部类对外是不可见的)。
注意:在方法中才能调用其局部内部类。
7)匿名内部类:
1.概念:匿名内部类是一种特殊的局部内部类,它是通过匿名类实现接口。
注意:匿名内部类没有名字,没有关键词 class,没有 extends 和 implements,也没有构造器。
2.一个匿名内部类一定是在 new 的后面,隐含着继承一个父类或者实现一个接口(或实现一个类),没有类名,根据多态, 我们使用其父类名。
3.利用父类或接口的名字代替 new 后面的名字来创建对象。
4.利用父类或接口声明变量:接口名 对象名 = new 接口名(){...};
注意:new 是一个完整的语句,注意一定要在花括号后加分号;
如果一个对象编译时的类型是接口,那么其运行的类型为实现这个接口的类。
5.因其为局部内部类,那么局部内部类的所有限制都对其生效。
注意:匿名内部类是唯一一种没有构造器的类。因匿名内部类无构造器,所以其使用范围非常的有限。
6.匿名内部类在编译的时候由系统自动起名 Out$1.class。
7、包装器类:Wrapper Class功能:将基本类型数据包装成对象
int i = 5; 或 String s = "5";
Integer integer = new Integer(i 或 s); //装箱(box):包装成对象
int j = integer.intValue(); //拆箱(unbox):提取数据
对于 String 型数据来说以上两步相当于:int j = Integer.parseInt(s);
8、集合 Collection
1)概念:集合是指一个可以容纳多个对象(不是引用)的对象,这个集合对象主要用来管理和维护一系列相似的对象。
2)集合三要素:若干个接口,接口的实现类和算法(一系列方法和实现类交合在一起)。
3)集合接口类层次:位于 package java.util.*;
注意:1.Set: 集合类中不允许有重复对象;
2.SortedSet: 和 Set 接口相同,但元素按升序排列;
3.List: 元素加载和移出时按照顺序,可以保存重复对象。
4.Map: 存储了唯一关键字辨识和对应的值(key-value 对)。
5.SortedMap: 和 Map 类相同,但对象按他们的关键字升序排列。
4)集合类层次 :
5)Collection 接口的方法:
add(Object o)
addAll(Collection c)
contains(Object o)
containsAll(Collection c)
remove(Object o)
removeAll(Collection c)
clear()
equals(Object o)
isEmpty()
iterator()
size()
toArray()
toArray(Object[] o)
6)Iterator(迭代器):读集合的一个工具
public interface Iterator{
boolean hasNext();
Object next();
void remove();
}
7)五个最常用的集合类之间的区别和联系:
1.ArrayList: 元素单个,效率高,多用于查询
底层是 Object 数组,所以 ArrayList 具有数组的查询速度快的优点以及增删速度慢的缺点。
2.LinkedList:元素单个,多用于插入和删除
a.经常在增删操作较多而查询操作很少的情况下使用:队列和堆栈。
队列:先进先出的数据结构;
栈:后进先出的数据结构。
b.在 LinkedList 的底层是一种双向循环链表。在此链表上每一个数据节点都由三部分组成:前指针(指向前面的节点的位 置),数据,后指针(指向后面的节点的位置)。最后一个节点的后指针指向第一个节点的前指针,形成一个循环。
c.双向循环链表的查询效率低但是增删效率高。
d.注意:使用栈的时候一定不能提供方法让不是最后一个元素的元素获得出栈的机会。
ArrayList 和 LinkedList 在用法上没有区别,但是在功能上还是有区别的。
3.Vector: 元素单个,线程安全,多用于查询
a.与 ArrayList 相似,区别是 Vector 是重量级的组件,使用时消耗的资源比较多。
b.结论:在考虑并发的情况下用 Vector(保证线程的安全);在不考虑并发的情况下用 ArrayList(不能保证线程的安 全)。
4.HashMap: 元素成对,元素可为空
5.HashTable: 元素成对,线程安全,元素不可为空
6. Map 遍历的形式:
// 1)
Set ks = map.keySet();
Iterator it = ks.iterator();
while(it.hasNext()){
Object key = it.next();
Object value = map.get(key);
}
// 2)
Collection values = map.values();
Iterator it = values.iterator();
while(it.hasNext()){
Object value = it.next();
}
// 3)
Set<Map.Entry> entrySet = map.entrySet();
for(Map.Entry entry:entrySet){
Object value = entry.getValue();
}
8)知识点:
1.java.util.stack(stack 即为堆栈)的父类为 Vector。可是 stack 的父类是最不应该为Vector 的。因为 Vector 的底层是数组, 且 Vector 有 get 方法(意味着它可能访问到并不属于最后一个位置元素的其他元素,很不安全)。对于堆栈和队列只能用 push 类和 get 类。故Stack 类以后不要轻易使用。
2.实现栈一定要用 LinkedList,collection 由 queue 来实现队列。
3.Set-HashSet 实现类:
a.遍历一个 Set 的方法只有一个:迭代器(interator)。
b.HashSet 中元素是无序的(这个无序指的是数据的添加顺序和后来的排列顺序不同),而且元素不可重复。
c.HashSet 底层用的也是数组。
d.在 Object 中除了有 finalize(),toString(),equals(),还有 hashCode()。当向数组中利用 add(Object o)添加对象的时候, 系统先找对象的 hashCode:
int hc = o.hashCode(); //返回的 hashCode 为整数值。
Int I = hc%n; //n 为数组的长度,取得余数后,利用余数向数组中
相应的位置添加数据,以 n 为 6 为例,如果 I=0 则放在数组 a[0]位置,如果 I=1,则放在数组a[1]位置。如果 equals()返回的 值为 true,则说明数据重复。如果 equals()返回的值为 false,则再找其他的位置进行比较。这样的机制就导致两个相同的对 象有可能重复地添加到数组中,因为他们的 hashCode 不同。
如果我们能够使两个相同的对象具有相同 hashcode,才能在 equals()方法中返回真。因此在实际中,我们定义 Student 对 象时要覆盖它的 hashcode。因为 String 类是自动覆盖的,所以当比较 String 类的对象的时候,就不会出现有两个相同的 string 对象的情况。现在在大部分的 JDK 中,都已经要求覆盖了 hashCode。
结论:如将自定义类用 hashSet 来添加对象,一定要覆盖 hashcode()和equals()这两个方法,覆盖的原则是保证当两个对象 相同时 hashcode 返回相同的整数,而且equals()返回的值为 True。如果偷懒,没有设定 equals(),就会造成返回hashCode 虽然结果相同,但在程序执行的过程中会多次地调用 equals(),从而影响程序执行的效率。
我们要保证相同对象的返回的 hashCode 一定相同,也要保证不相同的,对象的 hashCode 尽可能不同(因为数组的边界 性,hashCode 还是可能相同的)。
例子:
public int hashCode(){
return name.hashcode()+age;
}
这个例子保证了相同姓名和年龄的记录返回的 hashCode 是相同的。
e.使用 hashSet 的优点:
hashSet 的底层是数组,其查询效率非常高。而且在增加和删除的时候由于运用的 hashCode 的比较确定添加元素的位 置,所以不存在元素的偏移,所以效率也非常高。因为 hashSet 查询和删除和增加元素的效率都非常高。但是 hashSet 增删的高效率是通过花费大量的空间换来的:因为空间越大,取余数相同的情况就越小。HashSet 这种算法会建立许多无 用的空间。
f.使用 hashSet 类时要注意,如果发生冲突,就会出现遍历整个数组的情况,这样就使得效率非常的低。
4.比较:
Collections 类(工具类 ---> 全是 static 方法):
Public static int binarySearch(List list,Object key)
Public static void Sort(List list,Comparator com)
Comparator 接口:
Int compare(Object a,Object b)
Boolean equals(Object o)
注意:集合的最大缺点是无法进行类型判定,这样就可能出现因为类型不同而出现类型错误。解决的方法是添加类型的判断。
例子:
import java.util.*;
public class Test {
public static void main(String[] arg) {
ArrayList al = new ArrayList();
Person p1 = new Person("kevin");
Person p2 = new Person("robin");
Person p3 = new Person("terry");
al.add(p1);
al.add(p2);
al.add(p3);
Collections.sort(al,p1);
for(Iterator it = al.iterator();it.hasNext();){
Person p = (Person)it.next();
System.out.println(p.name);
}
}
}
class Person implements java.util.Comparator{
public String name;
public Person(String name){
this.name = name;
}
public int compare(Object a,Object b){
if(a instanceof Person && b instanceof Person){ //类型的判断
Person pa = (Person)a;
Person pb = (Person)b;
return pa.name.compareTo(pb.name);
}
return 0;
}
public boolean equals(Object a){return true;}
}
9)总结:谈谈 Java 中的集合框架?
1.ArrayList,Vector,LinkedList 的存储性能和特性?
2.Collection(集合的最上层接口)与 Collections(工具类)的区别?
3.怎样使 ArrayList 成为线程安全的?
根据集合存放数据的特点,分为两类:
1)只存单值: List , Set
ArrayList:以数组的方式管理数据,线程不安全的,适合于做大量的查询操作;
思考:怎么变成线程安全的(java.util.Collections)?
-----> synchronizedList(List<T> list)方法;
LinkedList:线程安全的,以双向链表的方式管理数据,适合于做大量的增加、删除操作;
Vector:不适合查询,线程安全的,适合处理多线程集合;
2)存 Key-Value 键值对: Map
HashMap(线程不安全的)
Hashtable(线程安全的)
TreeMap(根据 HashCode 进行排序)
9、反射(JAVA 中最抽象并且是最难理解的概念,也叫镜像)
1)作用:
1.动态构建类的实例:确定一个对象的类;
2.获得类的内部信息:获得一个类的修改符、变量、方法、构器函数、和父类的相关信息;
找出哪些常量和方法是从一个接口声明的;
创建一个在运行时才知道名称的类;
调用对象的方法;
3.改变类的一些特性。
2)获取镜像仅有的三种方式:
1.知道类的对象:
object o = xxxx;
Class clazz = o.getClass();
2.知道类名获取镜像:
java.uil.ArrayList
Class clazz = ArrayList.class;
3.知道存放类名的字符串:
String s = "xxxxx"; //不知类名
Class clazz = Class.forName(s);
3)通过反射修改类的特性:
1. 修改一个对象的实例变量值
Field.set(instance,value);
2. 调用一个对象上的方法
Method.invoke(instance,new Object[]{value1,value2});
3. 调用一个类中的构造器
Constructor.newInstance{new Object[]{value1,value2});
4)程序范例:
import java.util.*;
public class DynamicObject{
public static void main(String[] args) throws Exception {
if(args.length<1){
System.out.println("Usage: java "+
DynamicObject.class.getName()+" XXXX");
System.exit(0);
}
String className = args[0];
Class clazz = Class.forName(className);
Collection conn = (Collection)clazz.newInstance();
conn.add(new Integer(32));
conn.add(new Integer(6454));
conn.add(new Integer(-4242));
Iterator iter = conn.iterator();
while(iter.hasNext()){
System.out.println(iter.next());
}
}
}