您的位置:首页 > 编程语言 > Java开发

java 基础复习

2011-11-20 00:00 411 查看
26、数组的反射

Hashcode

集合类

在java编程中经常需要使用到一个容器性质的对象来储存其他的多个对象,习惯上将具有容器性质的类称为集合类,在一些编程应用中,程序运行期间向容器中最终添加的元素是无法提前预知的,例如:要将用户在屏幕上画得每条线储存到一个容器中,用户最终可能会画多少条线呢,这在编写程序和程序没有最终运行结束前十无法确定的,前面讲解数组是一个典型的容器,但是数组里面能够存储的元素个数在定义数组时就固定了,导致数组有如下缺陷,

1、程序在运行期间不断向数组中添加元素个数很可能会超出数组容量的大小

2、为了避免新定义一个新的大的数组,将初始化数组的容量定义很大,但程序运行期间最终添加的元素很少,就浪费了存储空间,

3、另外,如果要从数组中删除某个元素,所写的编码很麻烦

集合类主要位于java.util包中,他们的容量可以随着其中添加进的元素数量增长而动态增长。

hashcode方法和hashset类

如果想查找一个集合中是否包含某个对象,通常需要逐一取出每个元素与要查找的对象进行比较,当发现某个元素与要查找的对象进行equals方法比较的结果相等时,则停止继续查找并返回肯定的信息,否则,返回否定的信息,如果一个集合中有很多元素,譬如一万个元素,并且没有包含要查找的对象时,则意味着你的程序需要从该集合中取出一万个元素进行比较才能得出结论。有人发明了一种哈希算法来提高从集合中查找元素的效率,这种方式将集合分成若干个存储区域,每个对象可以计算出一个哈希码,可以将哈希码分组,每组分别对应某个存储区域,根据一个对象的哈希码就可以确定该对象存储的区域了。

object类中定义了一个hashcode()方法来返回每个java对象的哈希码,然后根据这个找到相应的存储区域,做好取出该存储区的每个元素与该对象的equal方法比较,这样不用遍历集合中的所有元素,可见hashset集合具有很好的对象检索性能。

Collection collections =new ArrayList();

reflectPoint pt1 =new reflectPoint(3,3);

reflectPoint pt2 =new reflectPoint(5,5);

reflectPoint pt3 =new reflectPoint(3,3);

collections.add(pt1);

collections.add(pt2);

collections.add(pt3);

collections.add(pt1);

再调用hashset方法

Collection collections =new hashset();

reflectPoint pt1 =new reflectPoint(3,3);

reflectPoint pt2 =new reflectPoint(5,5);

reflectPoint pt3 =new reflectPoint(3,3);

collections.add(pt1);

collections.add(pt2);

collections.add(pt3);

collections.add(pt1);

ArrayList是一个有序的集合。有序是按排队的先后位置放置,可以重复放。

当一个对象被存储进hashset集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则对象修改后哈希值与最初存储进hashset集合中的哈希值不同,这将导致无法从hashset集合中单独删除当前对象,从而造成内存泄露。

反射的作用,实现框架功能

框架与框架要解决的核心问题

我做房子卖给用户住,

先直接用new语句创建ArrayList和hashcode的实例对象,演示用eclipse自动生成ReflectPoint

类的equals和hashcode方法,比较俩个集合运行结果的差异,

然后改为采用配置文件加反射的方式创建ArrayList和hashset的实例对象,比较观察运行结果差异。

public static void main(String args[])throws Exception{

InputStream ips =new FileInputStream(''config.properties);

Properties props = new Properties();

props.load(ips);

ips.close();

String className= props.getProperty("className");

Collection collections =(Collection)(class.forName(className).newInstance());

27.框架与工具类的区别:工具类被用户的类调用,而框架则是调用用户提供的类。【框架就相当于房子,而门窗则相当于自己写的类,是房子调用门窗,而锁相当于工具类,是我的门在调用锁】

为什么说反射技术常用于开发框架,框架是再我们用之前就写好了,我们在应用是框架在调用用户提供的类,就是它不知道我们会写什么类,框架是这么实现调用的呢,这就需要用到反射了,

.接下来我们用类加载器的方式去管理资源和配置文件,properties等效于hashMap,内存中装的是键值对,可以把自己内存中键值存到硬盘上,也可以在初始化时把自己的值加载进来

动态传递类名

InputStream is = new FileInputStream("config.properties");

Properties ps=new Properties();

ps.load(is);

is.close();

String className=ps.getProperty("className");

Collection con1=(Collection) Class.forName(className).newInstance();

28、.类加载器实现对资源文件管理和配置

// 用这种方式获得资源文件,资源文件放在src外,一定要记住完整的路径,但完整的路径不是硬编码,而是运算出来的

// InputStream is = new FileInputStream("config.properties");

// 利用类加载器实现,前面要配置到具体的包下,只能读,不能修改,也就是没有输出流

// InputStream is

// =ReflectTest.class.getClassLoader().getResourceAsStream("cn/itcast/config.properties");

// 直接利用字节码文件加载

// InputStream is

// =ReflectTest.class.getResourceAsStream("config.properties");

// 放在子包下

InputStream is = ReflectTest.class

.getResourceAsStream("resources/config1.properties");

29、.内省:IntroSpector

javaBean是一种特殊的java类,一种其中含有get,set方法的类

如:

class People{

private int x;

public int getAge(){

return x;

}

public void setAge(int age){

this.x=age;

}

}

对于外面只可以看见其get和set方法,只能通过方法名得知,其中有一个age属性

Age---->如果第二个字母是小的,则把第一个字母变成小的--->age

如:gettime--->time

setTime--->time

getCPU---->CPU

.通过内省的方式得到一个对象的get和set方法。

例子:

package cn.itcast;

import java.beans.IntrospectionException;

import java.beans.PropertyDescriptor;

import java.lang.reflect.InvocationTargetException;

import java.lang.reflect.Method;

public class IntroSpectorTest {

public static void main(String[] args) throws Exception {

ReflectPoint pt1 = new ReflectPoint(2, 5);

String propertyName = "x";

Object o = 54;

setProperty(pt1, propertyName, o);

System.out.println(pt1.getX());

}

// 设置set方法

private static void setProperty(Object pt1, String propertyName, Object o)

throws IntrospectionException, IllegalAccessException,

InvocationTargetException {

// 得到属性描述,PropertyDescriptor 描述 Java Bean 通过一对存储器方法导出的一个属性。

PropertyDescriptor pd1 = getProperty(pt1, propertyName);

//获得应该用于写入属性值的方法

Method methodSetX = pd1.getWriteMethod();

methodSetX.invoke(pt1, o);

}

// 设置get方法

private static PropertyDescriptor getProperty(Object pt1,

String propertyName) throws IntrospectionException,

IllegalAccessException, InvocationTargetException {

PropertyDescriptor pd = new PropertyDescriptor(propertyName,

pt1.getClass());

Method methodGetX = pd.getReadMethod();

Object retVal = methodGetX.invoke(pt1);

System.out.println(retVal);

return pd;

}

}

**重构一个方法的步骤:

先是选中你要重构的方法--->鼠标点右键--->refactor-->选择方法的重构

例二:

// 设置get方法

private static Object getProperty(Object pt1,

String propertyName) throws IntrospectionException,

IllegalAccessException, InvocationTargetException {

/*PropertyDescriptor pd = new PropertyDescriptor(propertyName,

pt1.getClass());

Method methodGetX = pd.getReadMethod();

Object retVal = methodGetX.invoke(pt1);

System.out.println("retVal="+retVal);*/

//第二种方法得到get方法,通过introSpector得到

Object retVal=null;

BeanInfo beanInfo=Introspector.getBeanInfo(pt1.getClass());

PropertyDescriptor[] pds=beanInfo.getPropertyDescriptors();

for(PropertyDescriptor pd:pds){

if(pd.getName().equals(propertyName)){

Method methodGetX=pd.getReadMethod();

retVal=methodGetX.invoke(pt1);

System.out.println("retVal="+retVal);

break;

}

}

return retVal;

}

32、.beanUtils的好处:可以很方便的取值和设值,支持级联操作,对map也可以进行操作,javabean可以和map相互转换

//利用beanUtils类来实现取值和设值的功能,但是都是以String类型来设值和取值的

BeanUtils.setProperty(pt1, "x", 123);

System.out.println(BeanUtils.getProperty(pt1, "x"));

//data中有个setTime方法,我们可以理解data类中有个time属性,beanUtils可以

//实现级联设置值,对象pt1的属性data,然后data又是一个对象,data又有一个time属性

BeanUtils.setProperty(pt1, "birthday.time", "22");

System.out.println(BeanUtils.getProperty(pt1, "birthday.time"));

//propertyUtils也可以设值和取值,与beanUtils不同是,它是按原始类型进行设值和取值的

PropertyUtils.setProperty(pt1, "x", 867);

System.out.println(PropertyUtils.getProperty(pt1, "x"));

33、注解

--注解用于告诉java编译器一种信息,相当于一个标记,加上相当于给程序打了一个标记,看有什么标记,就采取相应的动作,标记可以加在,包,类,字段,方法的参数等上面,也可是是局部变量上面。一个注解就是一个类,

常用的有:压缩警告,继承父类--重写,过时

//压缩注解

@SuppressWarnings("deprecation")

public static void main(String[] args) {

System.runFinalizersOnExit(true);

}

//过时注解

@Deprecated

public static void sayWorld(){

System.out.println("hi world");

}

//继承父类

@Override

public String toString() {

return str1 + "," + str2 + "," + str3;

}

34.class文件不是字节码,只有当类加载器把class文件通过一定的处理并加载到内存中后才是字节码

注解的注解是元注解

.注解的生命周期:

java源文件阶段,class阶段,runtime运行时阶段(内存中的字节码),默认是class阶段; 【就是说注解是在什么情况下起作用】三种:在编写代码的时候(source),在编译的时候存在(class),在运行的时候存在(runtime)。

三种其实是一个枚举,枚举的名字是:RetentionPolicy

@Override src

@SuppressWarnings src

@Deprecated runtime

**class的父类是type,枚举上,注解上,类上,接口上

35.注解相当于一个类,其写法:

package cn.itcast.day2;

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)

// 注解的注解称为元注解,上面的设置时说此注解在运行时起作用,下面的注解是指定注解可以标在什么地方,第二个参数设置为type

// 是因为注解,接口,枚举,类的父类都是type,设置为type,说明注解可以写在注解,接口,枚举,类上

@Target({ ElementType.METHOD, ElementType.TYPE })

public @interface ItcastAnnotation {

}

引用注解的类:

package cn.itcast.day2;

@ItcastAnnotation

public class AnnotationTest {

// 压缩注解

@SuppressWarnings("deprecation")

@ItcastAnnotation

public static void main(String[] args) {

System.runFinalizersOnExit(true);

if (AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class)) {

ItcastAnnotation itcastAnnotation = AnnotationTest.class

.getAnnotation(ItcastAnnotation.class);

System.out.println(itcastAnnotation);

}

System.out.println();

}

// 过时注解

@Deprecated

public static void sayWorld() {

System.out.println("hi world");

}

}

.注解的属性:string,int,数组,class,annotation,enum.....

例子:

package cn.itcast.day2;

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

import cn.itcast.day1.EnumTest;

@Retention(RetentionPolicy.RUNTIME)

// 注解的注解称为元注解,上面的设置时说此注解在运行时起作用,下面的注解是指定注解可以标在什么地方,第二个参数设置为type

// 是因为注解,接口,枚举,类的父类都是type,设置为type,说明注解可以写在注解,接口,枚举,类上

@Target({ ElementType.METHOD, ElementType.TYPE })

public @interface ItcastAnnotation {

// 给注解增加属性,默认值直接给后面添加default "blue"

String color() default "blue";

// 特殊的value

String value();

// 属性是一个数组

int[] arrayAttr() default { 3, 2, 4 };

// 属性是枚举类型

EnumTest.TrafficLamp lamp() default EnumTest.TrafficLamp.RED;

// 属性是一个注解

MetaAnnotation annotationAttr() default @MetaAnnotation("kkk");

//属性是一个类

Class ss() default Student.class;

}

在另一个类中引用:

package cn.itcast.day2;

@ItcastAnnotation(color = "red", value = "aaa", arrayAttr = { 1, 3 }, annotationAttr = @MetaAnnotation("eeee"))

public class AnnotationTest {

// 压缩注解

@SuppressWarnings("deprecation")

// 因为有默认值,所以可以不设置,如果只有一个属性需要设置值,并且此属性名value,那么就可以直接设置其值

@ItcastAnnotation("liz")

public static void main(String[] args) {

System.runFinalizersOnExit(true);

if (AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class)) {

ItcastAnnotation itcastAnnotation = AnnotationTest.class

.getAnnotation(ItcastAnnotation.class);

System.out.println(itcastAnnotation);

System.out.println(itcastAnnotation.color());

System.out.println(itcastAnnotation.value());

System.out.println(itcastAnnotation.arrayAttr().length);

System.out.println(itcastAnnotation.lamp().nextLamp().name());

System.out.println(itcastAnnotation.annotationAttr().value());

}

System.out.println();

}

}

36.泛型generic:不用进行类型转换

没有使用泛型集合,只要是对象,不管是什么类型的对对象,都可以存储进同一个集合中。使用泛型集合,可以将一集合中的预算限定为一个特定类型,集合中只能存储同一个类型的对象,这样更安全,并且当从集合获取一个对象时,编译器也可以知道这个对象的类型,不需要对对象进行强制类型转换,这样更方便



1.5之后如果没有使用泛型,就会有一个unchect的警告

要改变一个类中某些相同的字段,如:

col1.add(3);

col1.add("fefer");

col1.add(334.43);中的col1点鼠标右键选择重构--选择name

package cn.itcast.day2;

/**

* 泛型的应用

*/

import java.lang.reflect.Constructor;

import java.util.ArrayList;

public class GenericTest {

public static void main(String[] args) throws Exception {

ArrayList col1 = new ArrayList();

col1.add(3);

col1.add("fefer");

col1.add(334.43);

// 虽然编译器可以通过,也就是你告诉编译器可以这么转换,但实际上是转换不过去的,所以运行时会报错

// ClassCastExecption

// int i1=(Integer) col1.get(1);

ArrayList<String> col = new ArrayList<String>();

// col.add(3);

col.add("fefer");

// col.add(334.43);

// 虽然编译器可以通过,也就是你告诉编译器可以这么转换,但实际上是转换不过去的,所以运行时会报错

// ClassCastExecption

String s = col.get(0);

System.out.println(s);

//泛型用在构造函数中,可以使构造函数不进行类型转化,不用指向具体的某个类型

Constructor<String> constructor=String.class.getConstructor(StringBuffer.class);

String str=constructor.newInstance(new StringBuffer("fer"));

System.out.println(str.charAt(1));

}

}

37、.编译器编译完以后,就会去掉其类型(泛型的类型),泛型是给编译器看的。可以通过反射通过泛型。

ArrayList<Integer> col2=new ArrayList<Integer>();

col2.add(4);

//泛型是给编译器看的,在运行时,会去掉泛型,也就是col1与col2是同一个字节码

System.out.println(col1.getClass() == col2.getClass());

System.out.println("=====================");

//不能给泛型中添加别的类型的数据,但可以通过反射来实现给泛型中添加别的类型的数据

System.out.println(col2.getClass().getMethod("add", Object.class)

.invoke(col2, "fdfd"));

System.out.println(col2.get(1));

//参数类型可以引用一个原始的对象,但编译器会有警告

ArrayList<String> col3=new ArrayList();

//原始类型可以应用一个参数类型,但编译器会有警告

ArrayList col4=new ArrayList<String>();

//下面代码是不对的,因为你规定的类型是String,但是实际里面装的什么都有,所以通不过,反过来也不行

//泛型中没有继承这一说

//ArrayList<String> col3=new ArrayList<Object>();

ArrayList col5=new ArrayList<Object>();

ArrayList<String> col6=col3;

38、.通配符:?表示任意类型

**限定通配符的上边界(表示的是number及number的子类):

vector<? extends Number> x=new Vector<Integer>;

**限定通配符的下边界(表示的是Integer及Integer的父类):

vector<? super Integer> x=new Vector<Number>;

// 泛型的综合应用

public static void getKeyVal() {

// 定义一个存放人名及年龄的集合

HashMap<String, Integer> map = new HashMap<String, Integer>();

map.put("zs", 32);

map.put("jian", 23);

map.put("yj", 34);

// 把集合中的元素都打印出来

Set<Map.Entry<String, Integer>> entrySet = map.entrySet();

for (Map.Entry<String, Integer> entry : entrySet) {

System.out.println(entry.getKey() + ":" + entry.getValue());

}

}

.一个map可以看做三个集合,key集合,value集合,Map.entry集合

// 泛型的使用,两个类型的交集是他们的最大公约数

private static <T> T add(T x, T y) {

return x;

}

// 利用泛型实现交换,T都是对象

private static <T> void swap(T[] a, int i, int j) {

T temp = a[i];

a[i] = a[j];

a[j] = temp;

}

//自定义泛型

public static <T extends Exception> void sayHi() throws T{

try {

} catch (Exception e) {

throw (T)e;

}

}

40.自定义泛型的例子

//把object转换为T类型的数

private static <T> T autoConvertType(Object obj){

return (T)obj;

}

//给一个任意数组中填充对应类型的任意数

private static <T> void fileArray(T[] desc,T src){

for(int i=0;i<desc.length;i++){

desc[i]=src;

}

}

//通配符的方法更有效,

private static <T> void printValue(Collection<T> col){

for(Object obj:col){

System.out.println("obj="+obj);

}

}

private static <T> void copy1(T[]desc,Collection<T> src){

for(Object obj:src){

if(obj!=null){

for(int i=0;i<desc.length;i++){

desc[i]=(T) obj;

}

}

}

}

//从一个数组赋值到相应数组

private static <T> void copy2(T[]desc,T[] src){

for(int i=0;i<desc.length;i++){

for(int j=0;j<src.length;j++){

desc[i]=src[j];

}

}

}

40.数据库的操作:crud增查修删 【dao data access object】

如果类的实际对象中的多处都要用到同一个泛型参数,既这些地方引用的泛型要保持同一个实际类型时,这时就要采用泛型类来定义。

静态方法不能用在泛型类中,如果一个类中多个方法用到泛型,那么这个类最好定义为泛型类。

泛型类的例子:

package cn.itcast.day2;

import java.util.Set;

public class GenericDao<T> {

public void add(T x) {

}

public T findById(T obj) {

return null;

}

public Set<T> findByConditions() {

return null;

}

public T findByName(String name) {

return null;

}

public void updateById(int id) {

}

public void updateByT(T obj) {

}

public void deleteById(int id) {

}

public void deleteByT(T obj) {

}

}

44.得到一个类的泛型类型:可以通过把这个类当做参数的方法而间接得到,主要用的是反射

代码如下:

GenericDao<ReflectPoint> dao=new GenericDao<ReflectPoint>();

dao.add(new ReflectPoint(3, 6));

//通过反射得到参数的类型

Method method=GenericTest.class.getMethod("applyVector", Vector.class);

//得到泛型的参数类型

Type[] type=method.getGenericParameterTypes();

//得到参数化的类型

ParameterizedType pt=(ParameterizedType) type[0];

//得到原始类型

System.out.println(pt.getRawType());

//得到实际参数类型

System.out.println(pt.getActualTypeArguments()[0]);

}

public static void applyVector(Vector<Date> v1){

}

45.加载类的工具称为类加载器。

类加载器也是一个java类,它是由BootStrap加载的,system也是有BootStrap

**BootStrap的孩子ExtClassLoader,孙子是AppClassLoader

**BootStrap负责加载JRE/lib/rt.jar

ExtClassLoader负责加载JRE/lib/ext/*.jar

AppClassLoader负责加载CLASSPATH指定的所有jar或目录

.类加载器的委托机制:

当java虚拟机要加载一个类时,

**首先当前线程的类加载器去加载线程中的第一个类。

**如果类A中引用了类B,java虚拟机将使用加载类A的加载器加载类B

**还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类

*****注意:每个类加载器加载类时,又是先委托给其上级类加载器【也就是孙子交给儿子,儿子又交个父亲,父亲看一下能处理就处理,如果处理不了,就交个儿子,儿子在判读,能处理就处理,如果不能处理,就交个孙子,孙子能处理就处理,处理不了就抛异常】

例子:

public static void main(String[] args) {

System.out.println(ClassLoaderTest.class.getClassLoader().getClass().getName());

//打印出来为null,说明System是一个特殊的类加载器,它不是一个类

//它嵌套在java虚拟机中

System.out.println(System.class.getClassLoader());

ClassLoader loader= ClassLoaderTest.class.getClassLoader();

while(loader!=null){

System.out.println(loader.getClass().getName());

loader=loader.getParent();

}

System.out.println(loader);

}

**打war包,点工程--》点鼠标右键--》export-->jar-->选择要打war包的文件

46.有包名的类不能调用无包名的类。

47一个简单的加密例子:

package cn.itcast.day2;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

public class MyClassLoader {

public static void main(String[] args) throws Exception {

String srcPath = args[0];

String descDir = args[1];

FileInputStream fis = new FileInputStream(srcPath);

String descFileName = srcPath.substring(srcPath.lastIndexOf('//'));

String descPath = descDir + "//" + descFileName;

FileOutputStream fos = new FileOutputStream(descPath);

cypher(fis, fos);

fis.close();

fos.close();

}

private static void cypher(InputStream is, OutputStream os) {

try {

int b = -1;

while ((b = is.read()) != -1) {

// 把0变1,把1变0

os.write(b ^ 0xff);

}

} catch (IOException e) {

e.printStackTrace();

}

}

}

46.自定义类加载器

package cn.itcast.day2;

/***

* 自己的类加载器,其中包含对文件的加密和解密

*/

import java.io.ByteArrayOutputStream;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

public class MyClassLoader extends ClassLoader {

public static void main(String[] args) throws Exception {

String srcPath = args[0];

String descDir = args[1];

FileInputStream fis = new FileInputStream(srcPath);

String descFileName = srcPath.substring(srcPath.lastIndexOf('//'));

String descPath = descDir + "//" + descFileName;

FileOutputStream fos = new FileOutputStream(descPath);

cypher(fis, fos);

fis.close();

fos.close();

}

// 实现加密的功能

private static void cypher(InputStream is, OutputStream os) {

try {

int b = -1;

while ((b = is.read()) != -1) {

// 把0变1,把1变0

os.write(b ^ 0xff);

}

} catch (IOException e) {

e.printStackTrace();

}

}

private String classDir;

@Override

protected Class<?> findClass(String name) throws ClassNotFoundException {

try {

System.out.println("myClassLoader");

String classFileName = classDir + "//"

+ name.substring(name.lastIndexOf('.') + 1) + ".class";

FileInputStream fis = new FileInputStream(classFileName);

ByteArrayOutputStream bos = new ByteArrayOutputStream();

// 进行解密

cypher(fis, bos);

fis.close();

byte[] bytes = bos.toByteArray();

return defineClass(bytes, 0, bytes.length);

} catch (Exception e) {

e.printStackTrace();

}

return null;

}

public MyClassLoader() {

}

public MyClassLoader(String classDir) {

this.classDir = classDir;

}

}

---------------------- android培训java培训、期待与您交流! ----------------------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: