JavaSE

JAVA SE 笔记

方法

方法名和参数列表相同时就视为相同方法

参数列表是指参数类型和类型声明的顺序相同

可变参数 (相当于以数组作为方法的参数)

1) 可变参数不能与数组构成重载

2) 可变参数只能放在参数列表的末尾

面向对象编程object oriented programming

封装 encapsulation

使用private 关键私有化成员,使外部不能随意访问对象的成员变量只能通过getset方法来访问,在方法中加入逻辑校验的功能,保证数据的合理,安全

继承 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 的是谁,就先调用谁的方法,也称虚方法的调用(动态绑定)

在多态中,属性不具备多态性如果BA中都有一个属性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) 抽象方法不能用staticfinalprivate关键字同时修饰,静态方法可以通过类名.方法名调用,抽象方法没有具体现实方式,故不能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 在转BooleanString只要不是“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方法

当且仅当两个元素的hashcodeequals方法比较结果为true时才视为重复 重复的元素将无法放入set

Treeset中的元素必须明确定义大小关系,即存入Treeset中的类必须实现Comparable接口

比较器 Comparator

用一个类实现Comparator接口,重写compareObject o1Object02)方法

将此实现类的对象作为参数传递给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;

    }

putdelete之后要维护一个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]

只允许泛型为NumberNumber子类的引用调用

 

<? super Number>      [Number , 无穷大)

只允许泛型为NumberNumber父类的引用调用

 

<? extends Comparable>

只允许泛型为实现Comparable接口的实现类的引用调用

IO

IO的结构体系

抽象基类 节点流 缓冲流(处理流的一种)

InputStream FileInputStream BufferedInputStream

OutputStream FileOutputStream BufferedOutputStream

Reader FileReader BufferedReader

Writer FileWriter BufferedWriter

对象序列化

对象序列化的意义在于可以将对象转化为二进制数据以便于传输

Statictransient修饰的关键字无法被序列化

ObjectOutputStreamObjectInputStream

ByteArrayInputStreamByteArrayOutputStream,这四个流为对象序列化核心的四个流

 

多线程

创建线程的三种方法

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();//拿到返回值

其中aCallable的一个实现类

线程的运行状态

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的使用

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值