JavaSE,
JavaSE,
JAVA SE 笔记
方法
方法名和参数列表相同时就视为相同方法
参数列表是指参数类型和类型声明的顺序相同
可变参数 (相当于以数组作为方法的参数)
1) 可变参数不能与数组构成重载
2) 可变参数只能放在参数列表的末尾
面向对象编程object oriented programming
封装 encapsulation
使用private 关键私有化成员,使外部不能随意访问对象的成员变量只能通过get,set方法来访问,在方法中加入逻辑校验的功能,保证数据的合理,安全
继承 Inheritance
extends
子类具有父类的成员属性和方法,但子类没有父类中的private 修饰的成员,也没有父类中的构造器,提高了代码的复用性,便于代码的维护
子类是父类的扩展,而不是子集,子类可以添加父类中没有的方法和属性
两个类之间具有所属关系例如:Student is a Person 那么student 类就可以继承person类,不能单单为了简化代码而使用继承
Java只能单继承,不支持多继承,一个类只能有一个直接父类,但是可以多层继承
当子类继承父类后,子类若出现与父类同名的属性时,当创建子类对象,调用该属性结果访问的是子类的属性。若子类需要使用父类该属性时,需要使用关键字:super。例如super.name(注意此处r后面有一个点);如果父类中没有name这个属性,那么在使用super.name会访问其父类的父类中的name属性;
方法的重写(override,也称覆盖)
1) 前提,要有继承关系:子类中定义了与父类方法签名一致的方法时,创建的子类对象在调用该方法时,调用的子类中的方法
2) 方法名和参数列表必须相同,
3) 返回值类型可以不同,但是有规则 返回值是引用数据类型时,在具备继承关系时,返回类型可以不同 如果有A extends B 则public A method() 是public B method() 的重写形式
子类重写方法时,不能缩小被覆写方法的访问的权限
继承之后子类所有构造器的第一行系统都会默认添加一句 super()调用父类无参构造器,如果父类没有无参构造器,子类构造器内可执行代码的第一行必须显式的调用父类有参构造器,super()和this(),必须出现在构造器的第一行,且不能同时出现,
4) 子类重写的方法抛出的异常类不能大于父类声明的异常类
多态Polymorphism
1) 方法的重载重载(只看参数列表)与重写的区别
重载方法的参数名相同,但参数列表不同即是重载,
2) 对象的多态性,父类的引用,指向子类对象
B extends A C extends A A a = new B(); A aa = new C();a.fun();是编译时如果A中没有fun方法那么编译无法通过,在A中添加fun方法,编译通过,运行时实际却是执行的子类中fun的方法,即编译时看左边,运行时看右边,。New 的是谁,就先调用谁的方法,也称虚方法的调用(动态绑定)
在多态中,属性不具备多态性如果B和A中都有一个属性num 在System.out.print(a.num);输出的是A中定义的num,即属性始终是以左边为准
不管声明时变量使用的什么类型,当通过变量调用方法时,方法的行为问题表现出它们实际类型的行为(new 的是哪种类型的对象就调用哪个该类的方法,)但如果通过这些变量来访问它们所指对象的实例变量,这些实例变量的值总是表现出声明这些变量所用的类型的行为
3) 引用数据之间的转换需要有继承关系,向上转型:子类对象转父类对象 向下转型:父类对象转子类对象,向下转型可能发生castException
4) Instanceof关键字,例如 a instanceof B,判断a是不是类B或其子类的对象
Man extends Person Women extends Person Person p = new Man(); Women w = (Women) p;此语句在编译时能通过,运行时报错
抽象abstract
将具体的事物用面向对象的思维方式将其用类描述
四种访问权限控制修饰符
Public :公共的,可以用于修饰属性、方法、类。任何地方都可以访问
Protected: 受保护的,可以用于修饰属性、方法。本类中、本包中、子类中
Default:默认的(缺省的)。不是关键字,当不写的情况下,就是默认的,可以用于修饰属性方法和类,只能在本类中,本包中
Private:私有的,可以用于修饰属性、方法。只有在本类中访问
“==”与equals()方法的区别
==对引用数据类型比较的是地址值,基本数据类型比较的是其值,例如:
int a =10;
int b =10;
double c =10.0
a==b 返回 true 。b==c 返回true
Person p1 = new Person();
Person p2 = new Person();
Person p3 =p1;
p1==p2 返回false
p3 ==p1 返回true
equals方法可以重写,默认的equals的方法体是:
public boolean equals(Object obj){
}
可以自己定义两个对象在何种情况下返回true,即可以重写equals方法
toString()方法
Object类中的方法,return this==obj;当直接输出对象引用时,默认调用object类中的tostring()方法,返回的结果是getClass().getName()+”@”+Integer.toHexString(hashCode());
Static
可以用来修饰方法,属性,代码块,内部类
1) static 修饰的属性,也叫类变量,随着类的加载而加载,随着类的消失而消失,静态属性的存在优于对象,被该类所有的对象所共享,可以通过“类名.变量名”使用
2) static 修饰的方法,也叫类方法,静态方法中不能使用非静态变量,非静态方法中可以调用静态成员,
利于static关键字,单例设计模式
单例设计的步骤
1) 私有化构造方法,
2) 类本身保存一个静态的本类的对象,并在内部为其初始化,
3) 通过静态方法取得对象,
两种单例设计模式,懒汉式,饿汉式,犹其要注意饿汉式在多线程调用时引发的问题
class Singleton{
private static Singleton instance =null;
private Singleton(){ };
public static Singleton getInstance(){
if(instance==null){
synchronized (Singleton.class) {
if(instance ==null)
instance = new Singleton();
}
}
return instance;
}
}
代码块
也是类的成员之一,用一对代码大括号包围起来的代码
非静态代码块
1) 直接用一对大括号包围
2) 在每次创建对象时调用
3) 优先于构造器调用
4) 可以有多个,依次向下地顺序执行
静态代码块
1) 左大括号前面加上static关键字
2) 随类的加载而被加载执行一次,优先于非静态代码块执行
3) 不能调用非静态成员
有以下代码:
public class Father {
/**静态变量会先初始化,但不会先先于static 代码块,
* 即使static变量的初始化语句写在satic 方法的后面,static 方法依然可以使用他
* */
public Father(){
System.out.println("父类的无参构造方法执行");
//System.out.println(b);
//Father.a();
}
static class Inner{
public Inner(){
System.out.println("内部类的无参构造方法执行");
//System.out.println(Test1.b);
}
static final int innera =32;
static void m(){
System.out.println("内部类的静态方法执行");
}
}
{
System.out.println("父类代码块执行");
System.out.println("父类代码块内的输出的"+b);
}
private static final String b ="father";
static {
System.out.println("父类静态代码块执行");
System.out.println("父类静态代码块内的输出"+b);
}
private int a =122;
public static void main(String[] args) {
Father t1 =new Son();
//Inner i = t1.new Inner();
}
public static void a(){
System.out.println(b);
}
}
class Son extends Father{
private static String son ="son";
public Son(){
//super();
System.out.println("子类无参构造方法执行");
}
{
System.out.println("子类代码块执行");
System.out.println("子类代码块内的输出的"+son);
}
static {
System.out.println("子类静态代码块执行");
System.out.println("子类静态代码块内的输出"+son);
}
}
执行结果:
父类静态代码块执行
父类静态代码块内的输出father
子类静态代码块执行
子类静态代码块内的输出son
父类代码块执行
父类代码块内的输出的father
父类的无参构造方法执行
子类代码块执行
子类代码块内的输出的son
子类无参构造方法执行
final
1) final 修饰的类不能被继承
2) final修饰的方法不能被重写 可以被重载
3) final修饰的变量叫常量(常量没有默认值,在作用前一定要初始化,直接显式赋值、构造器赋值,代码块赋值。常量名字母全部大写,多个单词之间以下划线隔开),一旦赋值就不能更改,但可以调用,
4) 通常用public static final VARNAME 这种形式,称为全局静态常量,
5) 方法的参数类型也可以用final修饰,方法内部就不能对参数进行更改操作
Abstract
1) 有一个或多个抽象方法就必须定义为抽象类
2) 抽象类不能实例化
3) 抽象的子类必须重写所有抽象方法,否则该子类还是一个抽象类
4) 抽象方法不能用static,final,private关键字同时修饰,静态方法可以通过类名.方法名调用,抽象方法没有具体现实方式,故不能static,final类不能被继承,private方法对其子类不可见
5) 抽象类可以有构造方法
6) 抽象类可以有private方法 但是private修饰的方法必需要有方法体 也就是说不能只声明一个private方法
接口Interface
1) 接口与类是平级的,使用关键字interface
访问控制修饰符 interface 接口{}
2) 接口可以理解为是一个特殊的抽象类,因为接口中只能定义全局静态常量(static final)和抽象方法
即使不在常量前不声明static final 编译器会默认有这两个关键字,方法前即使不声明abstract也会被默认为abstract
3) 接口中不能声明一般方法、代码块、构造器。
4) 接口不能创建实例
5) 一个类实现接口称为实现类,如果没有实现接口所有的抽象方法,这必须被定义为接口或是抽象类,只有实现所有的抽象方法才能实例化
6) 接口可以多继承接口,当接口在继承接口时,如果两个接口中有方法返回类型方法名相同的情况,依然会出现方法冲突,导致编译无法通过,interface A extends B{} B是一个接口
7) 一个类可以在实现多个接口的情况下继承另一个类class Bird extends Animal implements Flyer,Runner{}
工厂设计模式
内部类
成员内部类:一个类中声明另一个类。
1) 也是类的成员之一
2) 可以使用四种访问控制修饰符public default protected private
3) 也可以使用Static final
静态内部类的实例的创建 Person.Mobile pm = new Person.Mobile();非静态内部类的实例的创建
Person p = new Person(); Person.Computer = p.new Computer();可以理解为非静态内部类的构造方法是p的一个普通方法方法
4) 与普通的类拥有相同的特性
5) 静态内部类可以有静态成员,而非静态内部类则不能有静态成员;
6) 静态内部类的非静态成员可以访问外部类的静态变量,而不可访问外部类的非静态变量;
7) 非静态内部类的非静态成员可以访问外部类的非静态变量;
局部内部类,在方法里声明一个类
某个类只适用于当前方法,局部内部类要使用方法的局部变量,那么该中变量必须声明为final,
方法出栈的时候,变量就已消失,内部类会隐藏持有一个变量,方法结束时,内部类不能对该变量修改,所以必须声明为final
匿名内部类
interface Compare {
void show();
};
Compare c = new Compare(){
public void show(){
System.out.print(“匿名内部类”);
}
};
枚举类
该类有确定个数的对象才能定义为枚举类,用关键字enum声明
1) 私有化构造器
2) 类的内部创建对象,并定义为static final
注解
常见注解
@Override该方法必须是重写方法
@Deprecated用于描述该类,方法,成员是过时的,不推荐使用
@SuppressWarnings 用于抑制编译器警告
包装类
八种基本数据类型与包装类相互转换
基本数据包装后就可以调用方法
Integer 类提供了一个小缓存,缓存范围为一个字节-127~128,这个范围的Integer对象值相等时的==操作返回的是true,其值超过此范围的Integer对象值相等时用==操作返回的是false
String 在转Boolean时String只要不是“true”则都转成false
String 转换为基本数据类型
使用对应包装类的构造器
使用对应包装类两个静态方法:parseXxx();和valueOf()两个方法
String
不可变的字符序列
用双引号引起来的string 对象存入在字符串常量池中,且只会存入一份,当常量池中已经存在该字符串,则直接返回该字符串的地址,通过构造器new出来的对象存入在堆中
StingBuffer
可变的字符序列
是线程安全的,效率低
StringBuilder
可变的字符序列
是线程不安全的,效率高
Date类
创建一个日期格式类的对象 示例: SimpleDateFormat sdf = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);
创建一个日期类的对象 Date date = new Date( );
String str = sdf.format(date);
异常体系结构
java.lang.Throwable
--Error 错误 终点严重的错误 如系统错误
--Exception 异常 尽可能预知并处理的问题 如用户输入不匹配 网络中断
--编译时异常 受检异常
--运行时异常 非受检异常
try catch 语句块
对可能发生异常的语句用try catch 包围
catch可以有多个,一旦某个catch块匹配到了类型的异常 后面的catch块就不再执行 异常类型大的写在后面
finally 里面的代码块只要在其之前的catch块中没有终止程序 即使catch块中有return语句 finally一样会执行且在catch到异常后会优先执行finally
finally中的语句通常用于关闭资源
throws与throw
throws使用在方法的声明处,用于声明抛出的异常类
throw使用在方法内部,用于创建并抛出异常对象 代替return语句
集合
Collection接口
Set 元素没有索引,不能根据索引取元素,不可重复的集合
那么Set是如何确定两个元素是重复的呢
首先比较的hashcode再调用equals方法
当且仅当两个元素的hashcode和equals方法比较结果为true时才视为重复 重复的元素将无法放入set中
Treeset中的元素必须明确定义大小关系,即存入Treeset中的类必须实现Comparable接口
比较器 Comparator
用一个类实现Comparator接口,重写compare(Object o1,Object02)方法
将此实现类的对象作为参数传递给Treeset的构造器
List 元素可重复,可以根据索引取元素
集合的遍历
迭代器的使用
listIterator可以在迭代的过程中对集合进行增删操作 而Iterator在迭代过程中不允许进得增删操作 取得集合的迭代器对象 通过hasnext 和 next方法对集合进行遍历
Map
存放是以(key value)的形式存放的 只能由key取得value不能由valu取得key
Hashmap的底层也是用hashtable实现的,key不允许重复如果key重复会覆盖原来的键值对
Key是否重复也是原理和set判断重复的原理一样
Map的遍历
Map m = new HashMap()
方式一
//取得所有键的集合
Set set1 =tm.keySet();
for(Object obj : set1)
Syso(tm.get(obj)
再由s中的key取得每一个value
方式二
//将键值对都取出
Set set = (Set) tm.entrySet();
Iterator it = set.iterator();
while(it.hasNext())
System.out.println(it.next());
方式三
//取出所有的值的集合
Collection co = tm.values();
Iterator it = set.iterator();
while(it.hasNext())
System.out.println(it.next());
HashMap的实现
从源码看出是用有链地址法解决冲突 用一个transient Entry<K,V>[] table;数组保存每个entry,entry的结构是这样的:
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;
int hash;
/**
* Creates new entry.
*/
Entry(int h, K k, V v, Entry<K,V> n) {
后面内容略去....
可以看出entry是一个类似链表节点的结构,
/**
* Removes and returns the entry associated with the specified key
* in the HashMap. Returns null if the HashMap contains no mapping
* for this key.
*/
final Entry<K,V> removeEntryForKey(Object key) {
int hash = (key == null) ? 0 : hash(key);
int i = indexFor(hash, table.length);
Entry<K,V> prev = table[i];
Entry<K,V> e = prev;
while (e != null) {
Entry<K,V> next = e.next;
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
modCount++;
size--;
if (prev == e)
table[i] = next;
else
prev.next = next;
e.recordRemoval(this);
return e;
}
prev = e;
e = next;
}
return e;
}
TreeMap的实现
红黑树,
在put时可以看出,有比较左右孩子节点的过程
public V put(K key, V value) {
Entry<K,V> t = root;
if (t == null) {
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp;
Entry<K,V> parent;
// split comparator and comparable paths
Comparator<? super K> cpr = comparator;
if (cpr != null) {
do {
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
else {
if (key == null)
throw new NullPointerException();
Comparable<? super K> k = (Comparable<? super K>) key;
do {
parent = t;
cmp = k.compareTo(t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
Entry<K,V> e = new Entry<>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;
modCount++;
return null;
}
在put和delete之后要维护一个avl树就要对树的结构进行调整用到了这个方法
涉汲到平衡树的旋转问题,比较复杂参考数据结构相关书籍
/** From CLR */
private void fixAfterInsertion(Entry<K,V> x) {
x.color = RED;
while (x != null && x != root && x.parent.color == RED) {
if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
Entry<K,V> y = rightOf(parentOf(parentOf(x)));
if (colorOf(y) == RED) {
setColor(parentOf(x), BLACK);
setColor(y, BLACK);
setColor(parentOf(parentOf(x)), RED);
x = parentOf(parentOf(x));
} else {
if (x == rightOf(parentOf(x))) {
x = parentOf(x);
rotateLeft(x);
}
setColor(parentOf(x), BLACK);
setColor(parentOf(parentOf(x)), RED);
rotateRight(parentOf(parentOf(x)));
}
} else {
Entry<K,V> y = leftOf(parentOf(parentOf(x)));
if (colorOf(y) == RED) {
setColor(parentOf(x), BLACK);
setColor(y, BLACK);
setColor(parentOf(parentOf(x)), RED);
x = parentOf(parentOf(x));
} else {
if (x == leftOf(parentOf(x))) {
x = parentOf(x);
rotateRight(x);
}
setColor(parentOf(x), BLACK);
setColor(parentOf(parentOf(x)), RED);
rotateLeft(parentOf(parentOf(x)));
}
}
}
root.color = BLACK;
}
泛型
1) 泛型的作用
在集合中应用泛型 限定集合中元素的类型 只能存放某一相同类型的元素 更加安全 使得方法通用性更强
2) 自定义泛型类 接口 方法
自定义泛型类
public class DAO<T> {
public void add(T t){
}
public T get(int index){
return null;
}
@Test
public void test1(){
DAO<String> dao = new DAO<String>();
dao.add("AAA");
}
}
自定义泛型方法
//自定义泛型方法,对一个类型不确定的数组进行排序
public <E> E sort(E [] e){
return null;
}
3) 集合的泛型限定,避免强制类型转换
public void test1(){
List<String> list = new ArrayList<String>();
Map<String,Integer> map = new HashMap<String, Integer>();
map.put("AAA", 15);
map.put("AAA", 15);
map.put("AAA", 15);
Set<String> set = map.keySet();
Collection<Integer> c = map.values();
Set<Entry<String, Integer>> set1 = map.entrySet();
}
4) 通配符
虽然Object 是String的父类 但List<Object> 就不是List<String> 的父类,涉汲到泛型的擦除机制
List<String> list = new ArrayList<String>();
List<Object> list1 = new ArrayList<Object>();
public void print (List<Object> list){
//dosomething
}
此处并不能将list作为参数传递给方法print();即方法print(list);的调用是无法通过编译的,要解决这个问题,可以将方法定义为
public void print (List<?> list){
//dosomething
}
<?>
允许所有泛型的引用调用
举例:
<? extends Number> (无穷小 , Number]
只允许泛型为Number及Number子类的引用调用
<? super Number> [Number , 无穷大)
只允许泛型为Number及Number父类的引用调用
<? extends Comparable>
只允许泛型为实现Comparable接口的实现类的引用调用
IO
IO的结构体系
抽象基类 节点流 缓冲流(处理流的一种)
InputStream FileInputStream BufferedInputStream
OutputStream FileOutputStream BufferedOutputStream
Reader FileReader BufferedReader
Writer FileWriter BufferedWriter
对象序列化
对象序列化的意义在于可以将对象转化为二进制数据以便于传输
Static和transient修饰的关键字无法被序列化
ObjectOutputStream和ObjectInputStream
ByteArrayInputStream和ByteArrayOutputStream,这四个流为对象序列化核心的四个流
多线程
创建线程的三种方法
1) 继承thread类重写run方法,创建对象,调用start()方法
2) 实现runnable接口,重写run方法,创建对象a,将此对象a作为参数传递给new Thread(a).start()
3) 实现Callable接口 这种方式可以返回数据
方法1
FutureTask<Integer> future =new FutureTask<Integer>(a);
new Thread(future).start();
Integer integer = future.get();
方法2
ExecutorService threadPool = Executors.newSingleThreadExecutor();
Future<Integer> future = threadPool.submit(a);
future.get();//拿到返回值
其中a是Callable的一个实现类
线程的运行状态
JDK中用Thread.State枚举表示了线程的几种状态
要想实现多线程,必须在主线程中创建新的线程对象。Java语言使用Thread类及其子类的对象来表示线程,在它的一个完整的生命周期中通常要经历如下的五种状态:
新建: 当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态
就绪:处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件
运行:当就绪的线程被调度并获得处理器资源时,便进入运行状态, run()方法定义了线程的操作和功能
阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出 CPU 并临时中止自己的执行,进入阻塞状态
死亡:完成了它的全部工作或线程被提前强制性地中止
多线程安全问题
1) 同步方法,在方法声明处加上synchronized
2) 同步代码块
synchronized(obj){
//代码块
}
Obj为一个确定的对象,可以理解为一个线程运行到此处时对外声称自己要独占obj当其它线程要独占obj时必须等到obj被释放
3) 同步锁需要手动的释放锁
lock.lock();
Try{
//要同步的代码
}
finally{
lock.unlock();//一定要放在finally里面保证无论何何情况都能释放锁
}
反射Reflection
类本身也可以看作为对象
获取一个类的Class对象
Person p = new Person();
1) 使用运行时类的class属性Class c = Person.class;
2) 通过运行时类对象的getClass()方法 Class c = p.getClass();
3) 通过Class类中的静态方法Class.forName(“全限定名”);
4) 通过类加载器(了解)
反射常用方法
Socket
传送d:\\test.jpg这个文件到客户端
public class TestSocket1 {
@Test
public void client() throws Exception{
FileOutputStream fos = new FileOutputStream(new File("d:\\receive.jpg"));
Socket s = new Socket(InetAddress.getByName("127.0.0.1"),9999);
InputStream is = s.getInputStream();
byte [] buf =new byte[1024];
int len =0;
while((len =is.read(buf))!=-1){
fos.write(buf, 0, len);
}
fos.close();
s.close();
}
@Test
public void server() throws Exception{
FileInputStream fs = new FileInputStream("d:\\test.jpg");
ServerSocket ss = new ServerSocket(9999);
Socket s= ss.accept();
OutputStream os = s.getOutputStream();
byte [] buf =new byte[1024];
int len=0;
while((len =fs.read(buf))!=-1){
os.write(buf, 0, len);
}
fs.close();
ss.close();
s.close();
}
}
资源文件的加载路径问题
某一project目录结构如下图
加载三个位置不同的配置文件详细代码如下
package com.other;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import org.junit.Test;
public class TestClassLoader {
@Test
public void test1() throws Exception{
//通过类加载器加载配置文件,配置文件放在包内,这里用此方式比较
InputStream is =this
.getClass()
.getClassLoader()
.getResourceAsStream("day0824\\testInPackage.properties");
//如果放在src目录下则只需要文件名就行了
Properties pt = new Properties();
pt.load(is);
System.out.println(pt.getProperty("location"));
//System.out.println(pt.getProperty("password"));
System.out.println("通过类加载器加载");
}
@Test
public void test2() throws Exception{
//通过流的形式加载配置文件配置,配置文件放在包内,此时文件的路径就需要全路径名,比较麻烦
Properties pt = new Properties();
InputStream is= new FileInputStream("D:\\workspaceAtguigu\\day0823\\src\\day0823\\testInPackage.properties");
pt.load(is);
System.out.println(pt.getProperty("location"));
System.out.println("以流的形式加载");
}
@Test
public void test3() throws Exception{
//通过流的形式加载配置文件,文件放在当前工程目录下,只能以这种方式加载,不能以类加载器的方式
Properties pt = new Properties();
InputStream is= new FileInputStream("testInProject.properties");
pt.load(is);
System.out.println(pt.getProperty("location"));
System.out.println("以流的形式加载");
}
}
动态代理
JDK动态代理 被代理的类必须实现指定的接口
1.新建一个类,实现InvocationHandler接口,在重写方法中通过反射调用方法,在此类中的构造器中传入被代理类的对象,并作为实例变量引用
2.创建被代理类的对象,被代理的类必须实现某个接口,
3.通过Proxy.newProxyInstance()静态方法创建代理对象,
4.将代理对象强转为被代理类型
5.调用方法
动态代理的运用:
比较一个被代理的类中有以下方法:
public int add(int a, int b) {
// TODO Auto-generated method stub
return a+b;
}
现在想在这个方法调用之前输出”方法开始执行” 调用之后输出”方法调用完毕”
那么代码只能写成这样:
public static void main(String[] args) {
System.out.println("方法调用之前");
new CalculatorImpl().add(5, 9);
System.out.println("方法调用之后");
}
使用动态代理‚后在InvocationHandler实现类的重写方法这样:
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
Object result=null;
try {
System.out.println("方法调用之前");
result = method.invoke(targetObject, args);
System.out.println("方法调用之后");
} catch (Exception e) {
// TODO Auto-generated catch block
System.out.println("方法"+method.getName()+"执行出行异常");
e.printStackTrace();
} finally{
System.out.println("方法"+method.getName()+"执行结束");
}
System.out.println("方法结果"+method.getName()+"即将返回");
return result;
}
就可以实现与中相同效果,费尽周折这样做有什么好处呢?现在需求突然变了,不希望在方法调用前后输出信息了,对于就只能在代码中把所有的输出语句注释掉或删除,这个例子中只有两行,万一输出语句一多,那就很可怕了,显然的方式不利于代码的维护,这个时候使用‚的优势就很明显了,不需要对代码作太多改变,只需要在调用方法时写成这样的形式:
public static void main(String[] args) {
new CalculatorImpl().add(5, 9);
}
所有输出语句就都没有了,方便了许多
Cglib代理:不必实现特定接口 被代理类不能是final类
1.代理类实现MethodInterceptor接口 并实现接口有声明的方法
2.创建代理对象Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
// 回调方法
enhancer.setCallback();
// 创建代理对象
return enhancer.create();
JAVA SE中简单2d游戏的开发
画面需要实时更新,能响应鼠标键盘的操作,并在画面上有更新(如发射子弹)
思路:
定义继承JPanel面板的类A;为各种需要更新位置的游戏元素编写public void paintXXX(Graphics g)的方法,并在方法里计算各个元素的位置调用g.drawImage(Image img, int x, int y,ImageObserver observer);后绘制到面板上,在A中定义一个Timer,启动main函数,在main函数里创建游戏面板后,注册鼠标键盘的监听,调用schedule(TimerTask task, long delay, long period)
TimerTask t = new TimerTask() {
@Override
public void run() {
//一些逻辑代码
//周期性需要更新的图像位置,例如发射出去子弹位置的更新
}
repaint(); // 重绘更新画面,调用paint()方法
}
}
Eclipse的使用
相关文章
- 暂无相关文章
用户点评