android中的重要java知识点回顾1(枚举、反射、类加载器等)
第1节 课程价值与目标介绍
基础知识非常重要,基础知识的深入程度决定了你能力
学习能力很重要,在未来的工作生活中,这是最重要的能力
每个知识点,会看会学会写会教人,才能达到熟练掌握的程度
第2节 eclipse及IDE开发工具介绍
1)IDE:ItegrityDevelopment Enviroment,集成开发环境
2)JavaEE:javaEnterprise Edition:java企业版开发工具。
3)JMS:Java MessageService,Java消息服务,是一个Java平台中关于面向消息中间件的API,用于在两个应用程序间,或分布式系统中,发送消息,进行异步通信。
4)JMX:Java ManagementExtensions,Java管理扩展;是一个为应用程序、设备、系统植入管理功能的框架。
5)JNDI:Java Nameingand Directory Interface:Java命名和目录接口。
6)JDBC:Java DataBase Connectivity:Java数据库连接。
3、Myeclipse和eclipse的区别:
1)Myeclipse:是elipse的插件,用于开发javaEE的平台,为其打补丁,扩展其功能,可以开发JavaEE程序即web项目,但是现在Myeclipse也集成了eclipse的功能,可直接使用Myeclipse。
2)eclipse:是开发java的一款专业的IDE(集成开发环境),以前需要Myeclipse才能开发JavaEE程序,现在的eclipse的版本也可以不用再安装Myeclipse插件,直接可以开发JavaEE项目。
第3节 eclipse工程管理与快捷键配置
顶级的目录就是工程, 打开工程后 里面会有一个以包名分级展现的java源文件 以及这个工程要用到的第三方类
window preferences(首选项)就是你的喜好 java compiler 编译器(按哪个版本进行编译)
这里有两个设置 做开发时有两个阶段,一个是编译时阶段,一个是运行时阶段 要保证编译时和运行时用的版本一样,否则会报错
配置快捷键 General->keys,设置alt+/键进行内容提示时,要注意解除alt+/键原来的绑定关系,直接输入alt+/就可以找到它的绑定关系,删除绑定关系时也可以使用remove binding这个按钮
一个工作间包含多个工程,一个工作间的设置,影响这个工作简单的所有工程
新建工作间:
file switch workspace other 进行工作间切换
当当前状态下没有你要找的工作间,这时就可以重新建立一个新的工作间,会在硬盘上自动创建工作间
因为它在创建工作间时,要把原来的工作间关掉,要用eclipse 重新打开新的工作间,加载这个工作间特有的配置信息,所以花的时间要长一点。
这时就可以在工作间创建工程了。 file new project 在弹出的对话框中选择 java project(普通的java工程)
refactor 重构包名 右键点击包名
工程的名字要尽量用英语
要养成良好的编程习惯,都要有包名,前面是公司的网址,域名,倒过来写,去哪个公司面试就写哪个公司
工程 包名 类名 变量名 等等 最好都用英语 逼着自己将英语学好
面试时主考官不会去看你的代码,你的算法,人家就看你的整体效果
第4节 eclipse试图管理与程序调试
怎么调试一个变量
在编辑区域内单击右键 选中 debug as 所谓不同的透视图就是指有不同的若干个小窗口的集合
在不同的应用情景下 要调用一些不同的小窗口 只要切换到不同的窗口即可
若某个窗口没有找到 就 window show view去进行查找
对这些视图要会分类会揣测
第5节 eclipse的编译与运行环境
设置单个工程的javac与java。
高版本的java能否运行低版本的javac编译的程序? 能
低版本的java能否运行高版本的javac编译的程序? 不能
工作台和工程 类似于java中的继承与覆盖的关系(工作台上面的工程,所用的配置都是工作台的配置,但是它也是可以单独进行配置,覆盖掉工作台原有的配置)
第6节 eclipse中导入已有的工程
选择java模板代码之一
右键->Surround With->选择即可
编辑模板代码
Perferences-->Java-->Editor-->Templates-->New
try
{
${line_selection}//这里的选项是在InsertVariable选择的
}
finally
{
${cursor}//光标
}
第7节 eclipse中导入已有的工程
首先将希望要导入的工程放到工作台下面来,这个工作台下面就多了一个工程,但是eclipse就没有发现你放进来的那个工程 这时就需要将工程进行导入操作
file import general existing projects into workspace
这个存在飞工程要选择其存在的根目录,就导入了,这时要注意你导入工程jdk的安装目录 jar包是否一样 若不一样 则
右键点击工程 build path configure build path 在第三个 libraries中将jdk这个库删除掉 然后增加你自己的库 在这里进行增加时 要选择 add library 不要选add jar 因为这个是增加一个单独的jar包,对于你工程内部的jar包(你将某个jar包拷贝到你的工程中来了,就选add jar)
第8节 eclipse中的静态导入与编译器语法设置
import语句可以导入一个类或某个包中的所有类
import static语句导入一个类中的某个静态方法或所有静态方法
语法举例:
import static java.lang.Math.sin;
import static java.lang.Math .*;
import static java.lang.Math.max;
以前是导入一个类 现在 导入的是类下的一个方法,这时调用该方法时就可以省略类名了
第9节 可变参数与OverLoad相关面试题分析
可变参数的特点:
只能出现在参数列表的最后; (int x,int...args) 这样可以
位于变量类型和变量名之间,前后无空格都可以; (int x,int...args,int y) 这样不对
调用可变参数的方法时,编译器为该可变参数隐含创建一个数组,在方法体中以数组的形式访问可变参数
public static int add(int x, int... args) { int sum = x; for(int i=0;i<args.length;i++) { sum += args[i]; } return sum; }
面试题:
overload和override的区别 即,重载与重写
overload 两个参数的个数 列表 类型完全一样 只有返回值不一样 不行 还要说出为什么不行
override 如果父类的方法是私有的,子类写一个方法跟它一样 是不是叫重写 是不是叫覆盖 不对 这等于是一个全新的方法
不仅仅说两个的区别 要有一些很深入的东西
第10节 java5的增强for循环
语法:
for ( type 变量名:集合变量名 ) { … }
用变量逐一去取集合中的元素,取出来后就执行循环体
注意事项:
迭代变量必须在( )中定义!
集合变量可以是数组或实现了Iterable接口的集合类
举例:
public static int add(int x,int ...args) { int sum = x; for(int arg:args) { // 变量的名字叫arg 迭代的集合叫args sum += arg; } return sum; }
第11节 基本数据的自动拆装箱及享元设计模式
基本数据类型的自动拆箱与装箱
自动装箱:
Integer num1 = 12; 自动将一个基本数据 封装成一个Integer对象, 再赋值给引用变量
自动拆箱:
System.out.println(num1 + 12); 将num1转换成基本数据类型再进行加法 这个过程叫做拆箱
Integer i1 = 13; 变成137 又不是同一个了 因为大的数据使用频率比较低 就没有用享元模式
Integer i2 = 13;
System.out.println(i1 == i2);
基本数据类型的对象缓存:
Integer num1 = 12;
Integer num2 = 12;
System.out.println(num1 == num2);
Integer num3 = 129;
Integer num4 = 129;
System.out.println(num3 == num4); 数值超过后就不相同了
他们装出来的Integer对象是否是同一个
若在-128~127之间的数字 一旦将其封装成integer对象,就会将其缓存起来,当下次又要将一个整数封装成integer对象时,先看缓存池里有没有 有的话直接拿,这样就节省了内存空间,因为这些比较小的数使用频率会很高,反复的出现,一出现就把它装成一个对象,到最后对象会越来越多;这个对象本来就是不怎么改变的,对象又很小,大家又都是用它,又不去改变它的数据,没必要每一个数字对应一个对象
这是一种设计模式,叫做享元模式flyweight
当这个数据对象很小,又有多个地方要整出很多个这样的对象来,但搞出的对象在用的过程中都是一模一样的,那就搞一个就可以了
Integer num5 = Integer.valueOf(12);
Integer num6 = Integer.valueOf(12);
System.out.println(num5 == num6);
Integer.valueOf(12);这个方法可以将一个整数变成integer 这个不是自动装箱,而是我手动调用静态方法,把一个基本类型的整数变成一个包装类型的对象
第12节 枚举的作用介绍
enum
从1.5开始,将从C语言去掉的东西又给加上了
枚举的权重比较大
为什么要有枚举
问题:要定义星期几或性别的变量,该怎么定义?假设用1-7分别表示星期一到星期日,但有人可能会写成int weekday = 0;或即使使用常量方式也无法阻止意外。
枚举就是要让某个类型的变量的取值只能为若干个固定值中的一个,否则,编译就会报错,枚举可以让编译器在编译时就可以控制源程序中填写的非法值,普通变量的方式在开发阶段无法实现这个目标
枚举的高级应用
枚举就相当于一个类,其中也可以定义构造方法、成员变量、普通方法和抽象方法。
枚举元素必须位于枚举体中的最开始部分,枚举元素列表的后要有分号与其他成员分隔。把枚举中的成员方法或变量等放在枚举元素的前面,编译器报告错误。
带构造方法的枚举
构造方法必须定义成私有的
如果有多个构造方法,该如何选择哪个构造方法?
枚举元素MON和MON()的效果一样,都是调用默认的构造方法。
带方法的枚举
定义枚举TrafficLamp
实现普通的next方法
实现抽象的next方法:每个元素分别是由枚举类的子类来生成的实例对象,这些子类采用类似内部类的方式 进行定义。
增加上表示时间的构造方法
枚举只有一个成员时,就可以作为一种单例的实现方式
第13节 用普通模拟枚举的实现原理
定义一个新的类型,按这个类型定义的变量,指向的值只能是固定变量的值
public class WeekDay { private WeekDay()();//首先将构造方法创建成私有的 别人不能创建对象 public final static WeekDay SUN = new WeekDay();//定义一个静态变量 类型还是WeekDay 变量的名字 由于属于一种常量 final修饰,叫SUN 值为 new WeekDay();写出来七个就行了, 别人用时只能调用这个类的SUN常量 WeekDay weekDay = WeekDay.MON; //MON是一个常量,一个对象,是 WeekDay这个类型的对象, 所以 可以将它赋值赋给这个类型的引用变量weekDay 如果你要想给这个引用变量赋值,只能赋我在这个类里面定义好的那些常量 //不可以为这个常量指定我规定好的值以外的其他值 public WeekDay nextDay() { //这个方法不能是静态了, 这个是SUN对象身上的方法, if(this==SUN){ //假如当前的对象=SUN这个引用变量指向的对象 return MON; } else{ return SUN; } } public String toString() { // 打印枚举对象 return this == SUN?"SUN":"MON" ; }
每一个枚举元素都是一个对象
可以有若干公有方法或抽象方法。采用抽象方法定义nextDay就将大量的if.else语句转移成了一个个独立的类
第14节 java5的枚举的基本应用
public static void main(String[] atgs){ WeekDay weekDay2 = WeekDay.FRI; //用枚举定义变量 只能定义给定的元素 FRI相当于一个对象 枚举是一个类 枚举中的元素 就相当于这个类搞出来的实际对象 System.out.println(weekDay2); //自动实现了toString方法 System.out.println(weekDay2.name()); // 和上一句打印结果相同 System.out.println(weekDay2.ordinal()); //自己的排行数列 从0开始 System.out.println(weekDay.valueOf("SUN").toString());//把字符串变成对应的那个枚举元素 System.out.println(weekDay.values().length); //返回一个数组 将枚举中的元素一个个装进数组里的子类的实例对象,子类的构造方法内部要调用父类的构造方法,调用父类的有参数的构造方法 new Data(3000){}; //newData } public enum WeekDay{ //基本的枚举类 最简单 SUN,MON,TUE,WED,THU,FRI,SAT }
第15节 实现带有构造方法的枚举
//定义带有参数的构造方法 public enum WeekDay{ SUN(1),MON(),TUE,WED,THU,FRI,SAT;//元素列表必须位于所有成分之前, 构造方法要放在列表之后 //元素列表之后若有内容 则需要加; private WeekDay()(System.out.println("first");) //构造方法必须是私有的 空参数构造方法 private WeekDay(int day)(System.out.println("second");) // 接收参数的构造方法 使其调用某个元素 }
只要用到了枚举类,里边的静态变量都会被初始化, 执行 就会调用构造方法 所以会执行结果 first
只要用到了类 里边的静态代码都会执行,
在枚举元素后面跟上括号,就表示创建这个元素指向实例对象的时候使用哪个构造方法
第16节 实现带有抽象方法的枚举
public enum TrafficLamp{ //都是内部类 交通灯 RED(30){ // RED是由TrafficLamp的一个子类来写的 必须要完成父类的抽象方法 public TrafficLamp nextLamp(){ return GREEN; } }, // 这里就相当于 new子类的实例对象,并且调用父类有参数的构造方法实例对象new完后,用RED引用名称去引用对象 GREEN(45){ public TrafficLamp nextLamp(){ return YELLOW; } }, YELLOW(5){ public TrafficLamp nextLamp(){ return RED; } }; public abstract TrafficLamp nextLamp(); //这个方法的返回值还是这个类 private int time; private TrafficLamp(int time){this.time = time;} //把别人传过来的值给我自己的成员变量 }
每个元素是这个类的实例对象,这个实例对象不能直接拿到这个类去new,只能拿这个类的子类去new,new有个对象,并且对象的名字叫RED。
先定义三个元素 每个元素后面跟着一堆大括号 然后在大括号中写子类的实现代码
枚举 顺带复习内部类 运用到内部类的很多知识
提高一种编程思想
返回方法类型 可以是自己本身的类型
类里边可以定义静态常量 常量指向的结果就是自己这个类型的实例对象
枚举只有一个成员时,就可以作为一种单例的实现方式
如果要写单例,可以定义一个枚举,并且这个枚举里只有一个元素,因为枚举就是一个特殊的类,
为什么要用枚举来写单例
构造方法不用自己写,外部类是私有的,不带参数的,元素是自然的就new出来
第17节 透彻分析反射的基础-class类
(平时使用的是class 创建的类,这里所说的Class是一个大写的类.)
Java类用于描述一类事物共性,该类事物有什么属性,没有什么属性,至于这个属性的值是什么,则是由这个类的实例对象来确定的.
不同的实例对象有不同的属性值.Java程序中的各个Java类,它们是否属于同一类事物,是不是可以用一个类来描述这类事物呢?
这个类的名字就是:Class,要注意与小写class关键字的区别哦.^_^
Class类描述的信息:类的名字,类的访问数形.类所属的包名,字段名称的列表/方法名称的列表,等等.
学习反射之前,就要先明确Class这个类.
如何得到各个字节码对应的实例对象(Class类型)
1,类名.class 例如,System.class
2,对象.getClass() 例如, new Data().getClass();
3,Class.forName("类名"),例如Class.forName("java.util.Date");
9个预定义Class实例对象:
boolean byte char short int long float double void
判断是否为基本类型用.isPrimtive();
数据类型的Class实例对象
Class.isArray();
总之:只要是在源程序中出现的类型,都有各自的Class实例对象,例如int[] void
class ReflectTest { public static void main(String[] args)throws Exception { byteCode(); } public static void byteCode()throws Exception { //先来搞一下3份字节码 String str1 = "abc"; Class cla1 = str1.getClass(); Class cla2 = String.class; Class cla3 = Class.forName("java.lang.String"); sop(cla1==cla2); sop(cla1==cla3); sop("Primitive:"+cla1.isPrimitive());//判断一下是否是基本类型的字节码 sop(int.class.isPrimitive()); sop(int.class==Integer.class); sop(int.class==Integer.TYPE);//这个表示的是Integer内包装的基本类型 sop(int[].class.isPrimitive());//判断数组是否为基本类型. } public static void sop(Object obj) { System.out.println(obj); } }
第18节 反射-概念
反射:就是把Java类中的各个成分映射成相应的Java类
需要掌握的包括:
Filed(成员变量),Method(方法),Contructor(构造方法),Package(包)
并不是说用这些方法,而是用这些类型中的一个方法来表达,
比如
System.exit();
System.getProperties();
上面的方法都可以用一个类型表示,Method
Method-->methodObj1
Method-->methodObj2
一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示.
第19节 构造方法的反射应用
得到某个类所有的构造方法:
Constructor[] constructor = Class.forName("java.lang.String").getConstructors();
得到某个类一个构造方法://.getConstructor(参数.class,/*可以是可变参数*/)//如果没有可变参数JDK1.4以前.可以传入的是一个数组
Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);
注意,
1,想要得到某一参数的类型,
2,再调用这个方法的时候也要传递同样类型的对象.
创建实例对象:通常方法:String str = new String(new StringBuilder("abc");
------------:反射方法:String str = (String)constructor.newInstance(new StringBuilder("abc"));
代码演示:
//new String(new StringBuilder("abc"));下面利用反射写一个与本行一样的代码
Constructor constructor1 = String.class.getConstructor(StringBuilder.class);
String str3 = (String)constructor1.newInstance(new StringBuilder("abc"));
System.out.println(str3.charAt(1));
Class.newInstance()方法
例子:String obj = (String)Class.forName("java.lang.String").newInstance();
该方法内部先得到默认的构造方法,然后该构造方法创建实例对象.
该方法内部的具体代码是怎样实现的呢?用到了缓存机制来保存默认构造方法的对象.
第20节 Filed-成员变量的反射
Field类代表某个类中的一个成员变量
演示用eclipse自动生成Java类的构造方法
问题:得到的Field对象是对应到类上面的成员变量,还是对应到对象上的成员变量?类只有一个,而该类的实例对象有多个,如果是与对象关联,哪关联的是哪个对象呢?所以字段fieldX 代表的是x的定义,而不是具体的x变量。
为了来演示这个成员变量 要自己定义一个类,这个类里边写两个成员变量,
public class RefiectPoint{
private int x;
public int y;
public RefiectPoint(int x,int y){
super();
this.x = x;
this.y = y;
}
} 有了这个类后对其进行反射
RefiectPoint pt1 = new RefiectPoint(3,5); Field fieldY= pt1.getClass().getField("y"); // 得到一个字段 先要得到这个类的字节码,字节码中就有成员变量的信息字节啊就可以得到某个字段 //fieldY只是代表这个类字节码生成的变量 用pt1可以搞好多对象,每个对象身上都有一个fieldY 在pt1上面是5 在其他上面就不知道是多少了 fieldY不代表某个变量身上具体的值, //field Y的值是多少?是5,错! field y不是对象身上的变量,而是类上,要用它去获取某个对象上对应的值 System.out.println(fieldY.get(pt1)); //取出变量在某个对象身上的值 因为它代表是类上面的变量不是 //对象身上的 Field fieldX = pt1.getClass().getDeclaredField("x");// 只要是里边声明过的, getField方法只能 //看到可见的 fieldX.setAccessible(true); //设置可以访问 System.out.println(fieldX.get(pt1)); //这就叫暴力反射
Field类的作用:代表字节码里边的一个变量 不代表某个对象身上的变量 我们在知道有y这个变量后,在各个对象上去取对象身上那个y
反射的作用 换掉一个对象里的字段 可以改变对象的值, 你在配置文件里配置了很多东西,我自动扫描你的文件,将你一写内置文件的配置换掉
第21节 成员变量反射的综合案例
需求: 将任意一个对象中所有String类型的成员变量所对应的字符串内容中的"b"改成"a"; class ChangeValueText { public static void main(String[] args) { //作业,高新技术_21 changeStringValue(pt1); System.out.println(pt1.toString()); } private static void changeStringValue(Object obj)throws Exception { Field[] field = obj.getClass().getFields(); for(Field fiel : field){ //if(fiel.getClass().equals(String.class){} //因为是字节码比较,所以就一份,以后字节码都要用==哦比较哦 if(fiel.getType()==String.class){ String oldValue = (String)fiel.get(obj); String newValue = oldValue.replace('b', 'a'); fiel.set(obj, newValue); } } } }
第22节 成员方法的反射
Method类代表某个类中的一个成员方法 不是一个对象身上的方法
得到类中的某一个方法:
例子: Method charAt = Class.forName("java.lang.String").getMethod("charAt", int.class);
调用方法:
通常方式:System.out.println(str.charAt(1));
反射方式: System.out.println(charAt.invoke(str, 1));
如果传递给Method对象的invoke()方法的第一个参数为null,这有着什么样的意义呢?说明该Method对象对应的是一个静态方法!
jdk1.4和jdk1.5的invoke方法的区别:
Jdk1.5:public Object invoke(Object obj,Object... args) 可变参数
Jdk1.4:public Object invoke(Object obj,Object[] args),即按jdk1.4的语法,想要传多个参数时,需要将一个数组作为参数传递给invoke方法时,数组中的每个元素分别对应被调用方法中的一个参数,所以,调用charAt方法的代码也可以用Jdk1.4改写为 charAt.invoke(“str”, new Object[]{1})形式。 这个数组长度为1 里头装一个integer对象 值为1
Method类代表某个类中的一个成员方法 不是一个对象身上的方法 可以拿着这个方法去调用类中的对象 方法和对象时没有关系的 我们在调用这个方法时必须通过某个对象去调用,你得到了方法 这个方法和对象没有关系 而紧跟着调用某个对象身上的方法 先得到方法,然后再针对某个对象去调用这个方法
//str1.charAt(1); 现在想干的事是 调用str1身上的charAt(1)方法
Method methodcharAt=String.class.,getMethod("charAt",int.class)
这个方法是从String字节码身上拿出来的,getMethod这个方法接收两个参数,第一个参数是方法名字,第二个参数是(一个类里面同名字的方法,有多种重载形式,你到底想从这个多个重载方法当中选取哪一个 你平时写程序时靠什么来识别的,靠参数的列表,列表就包括了列数和类型 这里每一个参数类型都对应一个class来表示, 到底有多少个参数就看你在此处写多少个class
只有一个参数 int)
用反射进行调用
System.out.println(methodCharAt.invoke(str1,1)
调用一个方法一定是在某个对象身上 第一个参数就是那个字符串对象,在这个字符串对象身上调用CharAt 第二个是 你调用charAt 方法的时候,要传一个参数进去, 得到的里边有几个参数 这里也得有几个参数int.class
这就是由反射的方式得到这个字节码当中的方法,再拿这个方法作用于某个对象,invoke表示调用 调用这个方法, invoke这个方法执行调用的动作,
invoke是这个方法对象身上的方法 方法的调用
画圆 这个方法 画那个动作在圆身上
人在黑板上画圆,三个对象,人 黑板 圆 画圆时需要哪些参数 信息 需要圆心和半径,圆心和半径是圆身上的,那画圆这个方法要用到圆心和半径, 圆心和半径是私有的,如果你将画圆这个方法分配给了人,通过人要去访问圆里边的私有方法,不合适,通过黑板也不合适,只有分配给圆,圆有一个动作叫画我, 画是我的动作,我才知道怎么画,
所以你调用的时候会感到别扭,circle.draw();这时给圆发信号 把自己画出来,你自己知道怎么画 我不知道
列车司机把列车给刹住了,想一想,列车司机有这么大的功力吗?
他只是踩了离合器,实际上是在给列车发出信号,你停车吧,你的离合器系统就开始运作,你的发动机熄火,所以是列车停车,司机给列车发信号,这个停车的动作只有列车才能干的了,所以画圆的动作只有圆能做的了,比如关门,谁把门关上,门 你只是推了一下,不是你关的,是它自己旋转,给关上的
面向对象其实非常简单:只要把变量搞成私有的,如果谁要操作这个变量,那么这个变量在谁身上,方法就应该在谁身上。这叫专家模式 谁拥有数据,谁就是干这个的专家, 那么这个方法就应该分配给谁
所以你看到的这个invoke是这个methodCharAt方法的invoke 但是调用invoke的时候是str1对象身上调用 一调用还要给参数
invoke(null,1) 若接收的方法为null 这时此方法是静态的 或者说 又一个静态方法 你想调用, 在这里写上null 因为静态方法调用时不需要对象, 不需要对象肯定是静态方法,
第23节 接收数组参数的成员方法进行反射
目标:
写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。用普通方式调完后,大家要明白为什么要用反射方式去调啊?
问题:
启动Java程序的main方法的参数是一个字符串数组,即public static void main(String[] args),通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,javac会到底按照哪种语法进行处理呢?jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。所以,在给main方法传递参数时,不能使用代码mainMethod.invoke(null,new
String[]{“xxx”}),javac只把它当作jdk1.4的语法进行理解,而不把它当作jdk1.5的语法解释,因此会出现参数类型不对的问题。
解决办法:
mainMethod.invoke(null,new Object[]{new String[]{"xxx"}});
mainMethod.invoke(null,(Object)new String[]{"xxx"});编译器会作特殊处理,编译时不把参数当作数组看待,也就不会数组打散成若干个参数了
我给你的数组,你不会当作参数,而是把其中的内容当作参数。
Class clazz = Class.forName(arg[0]);
Method mMain = clazz.getMethod("main", String[].class);
mMain.invoke(null,new Object[]{new String[]{"aaa","bbb"}});
mMain.invoke(null,(Object)new String[]{"aaa","bbb"});
}做一个程序 调用这个main方法
平时 TestArguments.main(new String[]{"111","222","333"}); 还可以传参数
这就是在程序里面用静态代码的方式直接调用它的main方法
直接调用 TestArguments类的main方法, newString类型的数组就搞定了
首先明白一个问题 为什么要用反射的 方式调用main方法
我并不知道要调用的那个类的名字, 你在执行这个main方法时同样给那个main方法中传进来一些参数, 而这个方法中就带有说 你去启动哪个类,假设里边第一个参数就是那个类的名字, 你告诉我 要去执行哪个类,我知道args[0]就是要启动的那个String className=args[0]; 类名就是String startingclassName=args[0];即将启动的className 那么我的源程序里边就没有出现到底是哪个类名,你通过一个参数给我说执行a这个类,我就执行a这个类,你在运行时说执行b这个类,我就执行b这个类,我的源程序中根本就不知道要执行哪个类,接下来你把一个类给我了,我知道这个类里面一定有一个方法
Method mainMethod = Class.forName(startingClassName).getMethod(name,parameterType) ("main",String[].class)
接收一个参数 并且是一个String类型的参数
接下来就要去调用main方法
mainMethod.invoke(null,new String[]{"111","222","333"});
静态的不需要传递对象,不通过对象就调用这个方法 要传递上面的参数 这样会报错 数组角标空指针异常
jdk1.5为了兼容jdk1.4, 当你给它一个字符串数组,(这就相当于object数组)。当收到一个object数组后不会将其当成一个参数,会将数组以包的形式打开,将打开的东西中的每一个元素分别作为一个参数(以数组的角度看就是一个参数,若将其看成一包东西就是好多个参数)
mainMethod.invoke(null,new Object[]{new String[]{"111","222","333"}});
这时再打一个包,new一个数组 这个数组里面装的第一个元素就是一个 数组 每个数组的父类都是object 就是每个数组都是object 是一个对象 现在给你一包东西 一打开 是一个数组
就是说 会将你拆开,这时就先把整个数组装成一个包,你拆包,露出来的就是数组
mainMethod.invoke(null,(Object)[]new String[]{"111","222","333"});
黎老师方法
因为数组也是一个Object 将其强制转换成Object(Object)这样写就相当于在和编译器说,我给你的是一个对象,不是一个数组,别拆包
说明类方法如何调用:因为我们要传一个数组,而数组在jdk1.4中被当做多个参数,我们想办法让其不让当做多个参数,
一种是 把数组打包成另外一个数组 你拆完一层皮后剩下来的就是那个数组 还有一种就是直接类型转换 还是当成object不要拆
第24节 数组与Object的关系及其反射类型
具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。
代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。
基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。
Arrays.asList()方法处理int[]和String[]时的差异。
Array工具类用于完成对数组的反射操作。
数组也是一种类型,由jdk帮助文档能够知道数组的类型 每一个数组都属于同一个class 如果你用反射,反射出来的字节码都是同一个(具有相同的元素类型以及具有相同的维度)
int[] a1 = new int[3]; //有一个数组 里面装的是int int不是object 基本类型不是object int[] a2 = new int[4]; int[] a3 = new int[2][3]; String [] a4 = new String[3]; //有一个数组,里面装的是String,String也是object //这是对象 对象要得到字节码 要用方法 System.out.println(a1.getclass() == a2.getclass());//具有相同的维数 都是一维数组 且具有相同的类型 true System.out.println(a1.getclass() == a4.getclass());//不等于 false System.out.println(a1.getclass() == a3.getclass());//不等于 false System.out.println(a1.getClass().getName()); //打印类名 [I 数组 整数 System.out.println(a1.getClass().getSuperclass().getName()); //打印父类的名字 System.out.println(a4.getClass().getSuperclass().getName());
Object aObj1 = a1; Object aObj2 = a4; Object[] aObj3 = a1; //基本类型的一维数组不能转换成object数组的 因为Object[]里边装的是int 不是obj //若是二维的 Object[]这个数组里装的是一维数组 一维数组是object
Object[] aObj4 = a3; //a3是一个数组的数组,假设将其当做一个数组,=左边的object[]就等于第二维的那个数组 把a3 当做一个数组来看待 它里边装的就是int类型的一维数组 而这个int类型的一维数组就是object类 这表示一个数组,数组里边装的是object 也可以将其理解成 有一个数组,数组里头装的是int类型的数组,就等效于有一个数组,数组里边装的是object
Object[] aObj5 = a4; //这个数组里边装的是String String也是Object
int[] a1 = new int[]{1,2,3}; int[] a2 = new int[4]; int[] a3 = new int[2][3]; String [] a4 = new String[]{"a","b","c"}; System.out.println(a1); //[I@1cfb549 int类型数组的哈希code值 System.out.println(a4); // [ljava.lang.String;@186d4c1 String类型的数组的哈希code值
arrays类 对数组进行操作的类
转换成list对象 aslist
System.out.println(Arrays.asList(a1));//[[I@1cfb549] System.out.println(Arrays.asList(a4)); // a b c 字符串成功的转换成了List对象 数组等效于多个元素
asList
public static List asList{object[] a} 后面结束的是object[] 如果是一个String类型的数组传进来,就按jdk1.4编译成List 如果传的是int类型,人家觉得跟这个对象对不上 就让你回1.5去处理
public static <T> List<T> asList(T...a) 不当做object数组类型 只当做一个object 就等效于一个参数 刚才的String类型的数组,符合1.4的 就把String数组里边的每一个元素列举出来放到list中去 如果你是int类型的数组, 不符合就按1.5的走,当成一个object 一个参数
第25节 ArrayList_HashSet的比较及Hashcode分析
public static void main(String[] args){ Collection collectons = new HashSet(); // 使用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); System.out.println(collections.size()); // 3 } public static void main(String[] args){ Collection collectons = new ArrayList(); // 使用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); System.out.println(collections.size()); // 4 } collections.add(new ReflectPoint(3,3)); // 也可以定义称为独立的变量 向上面一样
上面只是因为使用容器不同造成打印结果的集合长度不同
ArrayList是一种有顺序的集合 就相当于一种数组,当你要放一个对象要进来的时候,它首先找到第一个空位置放进去,不是真正的把对象放进去了 而是将对象的引用在数组中记住了 当你再放一个对象进去时 它会按顺序找到第二个位置 当你放第三个时 第三个与第一个相同 还放的进去,就相当于 每放一下里边就多一个引用变量,好多个引用变量 引用的是同一个对象 没关系 它是按照先后顺序依次放进去,这样就是有顺序的 你可以明确的说从第几个取,有位置顺序
甚至可以插队 这个顺序不是比较顺序(排序),而是指位置顺序 每放一个 每一个都能放进去
HashSet(); 你要放进去的时候,先判断里面有没有这个对象,就是比较两个对象是否相等,如果一旦有了 就不放;如果你要想放一个对象覆盖掉原来的对象(你必须要把原来的删除掉remove)再把新的给插进去
如果想要让pt1和pt3相等 必须要去写equals方法 否则 默认 equals比较的是hashcode的值 通常是由内存地址换算出来的
这是两个独立的对象,==肯定不等,equals我们没有覆盖 所以也不等
面试题
hashCode方法的作用 如果想查找一个集合中是否包含有某个对象,大概的程序代码怎么写呢?你通常是逐一取出每个元素与要查找的对象进行比较,当发现某个元素与要查找的对象进行equals方法比较的结果相等时,则停止继续查找并返回肯定的信息,否则,返回否定的信息。如果一个集合中有很多歌元素,譬如一万个元素,并且没有包含要查找的对象时,则意味着你的程序需要从该集合中取出一万个元素进行逐一比较才能得到结论。有人发明了一种哈希算法来提高从集合中查找元素的效果,这种方式将集合分成若干个存储区域,根据一个对象的哈希码就可以确定该对象应该存储在哪个区域
只有说文件的存储集合必须存储在这种hash集合当中才有这种价值
HashSet就是采用哈希算法存取对象的集合,它内部采用对某个数字n进行取余的方式对哈希吗进行分组和划分对象的存储区域。Object类中共蒂尼了一个hashCode()方法来返回每个java对象的哈希吗。当从HashSet集合中查找某个对象时,java系统首先调用对象的hashCode()方法获得该对象的哈希码,然后根据哈希吗找到相应的存储区域,最后取出该存储区域内的每个元素与该对象进行equals方法比较,这样不用遍历集合中的所有元素就可以得到结论,可见,HashSet集合具有很好的对象检索性能,但是,HashSet集合存储对象的效率相对要低些,因为向hashset集合中添加一个对象时,要先计算出对象的哈希吗和根据这个哈希吗确定对象在集合中的存放位置
没有实现hashcode算法就=3
因为如果 这两个对象比较equals比较相等了 但是你算出来的hashcode值是按照你的内存地址算的 这两个本来该认为相同的对象,分别被存放到了不同的区域,当我要去找这个对象的时候,我在我这个区域里面找,不在那个区域里边找,那个区域确实存在一个跟我相等的对象,但是我不去那个区域里边找,我就被放进数组中去了。
为了让相等的对象也放在相同的区域,所以人家就有一个说法 如果两个对象equals相等的话,应该按照他们的hashcode也相等,如果对象不存到hash集合里边,就没必要搞hashcode了
当一个对象被存储进hashset集合中以后,就不能够修改这个对象中的那些参与计算哈希值的字段了,否则,对象修改后的哈希值与最初存储进hashset集合中时的哈希值就不同了,在这种情况下,即使在contains方法使用该对象的当前引用作为的参数去hashset集合中检索对象,也将返回找不到对象的结果,这也会导致无法从hashset集合中单独删除当前对象,从而造成内存泄露
public static void main(String[] args){ Collection collectons = 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); pt1.y = 7; //改完后就取不走了 原来的数据找不着了 就叫做 内存泄露 collections.remove(pt1); System.out.println(collections.size()); }
java中在内存泄露吗,为什么?
所谓内存泄露就是这个对象我不要用了 却还在一直占用内存空间,没有被释放,这就叫做内存泄露
举例就是上面的例子
第26节 框架的概念及用反射技术开发框架的原理
什么是框架,例如,我们要写程序扫描.java文件中的注解,要解决哪些问题:读取每一样,在每一个中查找@,找到的@再去查询一个列表,如果@后的内容出现在了列表中,就说明这是一个我能处理和想处理的注解,否则,就说明它不是一个注解或者说至少不是一个我感兴趣和能处理的注解。接着就编写处理这个注解的相关代码。现在sun提供了一个apt框架,它会完成所有前期工作,只需要我们提供能够处理的注解列表,以及处理这些注解的代码。Apt框找到我们感兴趣的注解后通知或调用我们的处理代码去处理。
你做的门调用锁,锁是工具,你做的门被房子调用,房子是框架,房子和锁都是别人提供的
程序中不处理异常,而是main方法声明抛出异常,便于大家可以集中看主要的关键代码。
Class类也提供getResourceAsStream方法的比喻:如果你每次都找我给你商店买可乐,那我还不如直接向你买可乐,即直接提供一个买可乐的方法给你。
String startingclassName=args[0];就是要调用某个类的main方法,而到底调用哪个类的main方法,在写程序的时候是不知道的,而是等程序运行起来以后,你再传递一个字符串给我, 这个字符串对应的这个类和现在这个类哪个先写出来的 args这个类还没写出来,源程序就编好了(我要调用你的类,结果我写程序的时候 你的类还没有写,我就可以调,只要你等我运行的时候写出来就可以,在我写的时候你还没有写出来,没关系 框架 用strans做框架, 在还没有用strans做项目的时候 框架就已经写完了 在用strans做项目时就写了好多个我们自己的类,我们写的类在运行的时候给strans框架区调用 框架先写完 我们的程序后写 这就是反射的一个好处)
大家以后在用别人写的类 有两种使用方式:一种是你去调用别人的类,还有一种是 别人的类来调用你的类 不管怎么弄 都是你在用别人的类 , 别人调用你 也算你用别人的 你用strans框架 是不是 框架在调用你 这两种有区别 一个叫框架 一个叫工具 举个买房子的例子
框架和工具类的区别: 都是别人写的 但是用法不一样 一个是 人家调用你 一个是你调用人家
大家做的一个一个的strans项目,每个项目都不一样,但是每个人都做的很快,因为大家首先上来就是在一个框架基础上开始干(在一个半成品的基础上开始),
人家找你们开发用框架 因为做事效率高,项目完成效率高
框架要解决的核心问题是: 要解决 我若干年前写程序 能够调用你若干年后写的程序 不能直接new某个对象,只能.class().forName()一个字符串,那个字符串等运行的时候再给我 这就是反射
public static void main(String[] args)throws Exception{ InputStream ips = new FileInputStream("config.properties"); Properties props = new Properties();/* 定义一个Properties对象,就等效于hashmap 内存里边装的是键值对 但是它在hashmap基础上扩展了一点功能 它可以将自己内存中的键值对存到硬盘的文件里边去,它也可以在初始化的时候 从一个文件里边将自己的键值对加载进来, 我们有一个空的hashmap 就得一个个手动的填充值,而这个Properties上来就可以从一个文件里边搞出来一堆键值对。*/ props.load(ips); ips.close(); /*良好的习惯 关门 否则会有一点小小的内存泄露 这个内存泄露不是 这个ips对象不被释放,而是这个对象关联的系统资源没有被释放(比如桌面上的window,是操作系统上的一个窗口,在内存上是占内存的。现在写一个java程序 c window 由这个java对象对应这个窗口,现在内存中就有两个东西,一个是原始的窗口,一个是java对应的对象,你能看到的就是java对象变成垃圾进行回收了,但是那个窗口还在桌面上 在程序里边要操作window 需要一个对象,代表实际窗口的对象,由垃圾回收器管理,操作系统的窗口由操作系统管理,调用close是在跟操作系统说把窗口关掉 但是本对象还在 本对象以后由java虚拟机作为垃圾回收 就是自己在被回收之前,先把自己关联的物理资源关掉 如果不关掉 就是你这个对象没有了,但是原来你指向这个操作系统的资源还在,),这句话不是将ips对象关掉,这个对象是由java虚拟机垃圾回收机制管理的,而是将资源释放了*/ String className = props.getProperty("className");//这句话中的两个class必须要一样, 起名字 尽量有意义 Collection collections = (Collection)Class.forName(className).newInstance(); //调用不带参数的构造方法类加载器 //Collection collectons = new ArrayList(); // 所谓反射就是这里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); //pt1.y = 7; //为什么这个现在能删呢 因为和hashcode没关系, 要是hashset就删不了 //collections.remove(pt1); System.out.println(collections.size());
第27节 用类加载器的方式管理资源和配置文件
配置文件: new file 名字:config properties 里边写 className=java.util.ArrayList 4
className=java.util.HashSet 2
等程序最终运行的时候,就不用改java源程序,只要改这个文件,那个类就换了
在运行的时候,我把程序交给了你,你作为客户你没有javac,不能改源文件,但是你又记事本,将这个文件改一下就可以了。
配置文件要放在那里?要放在工程下面
程序开发完后,不能将整个java目录给对方,只要将.class文件打包成jar包给对方就行,在实际开发中没有用相对路径搞得,你不知道这个相对路径是相对于谁
c:\Documents and Settings\ibm>java.MyClass xx.file 在这个路径下用到了xx.file文件,xx文件是相对于MyClass文件还是java.exe 谁都不是,而是相对于当前工作目录ibm下 这时就感觉到这个相对路径是飘忽不定的,要用就用绝对路径
而用绝对路径又出现一个问题: 一定要记住用完整的路径,但完整的路径不是硬编码,而是运算出来的
InputStream ips = new FileInputStream("d\\config.properties"); 将其放在d盘,结果人家机器上小硬盘 没有d盘,只有c盘, 这是一种解决方案,绝对路径,但是绝对路径这个d盘,不是硬编码写进去的。而是用某种方法get出来的。 譬如说你在一个配置文件里边, 而对方在系统的某个地方进行配置,说config.properties的位置 所在的目录,配完后,就可以去读 也就是说用户想把它放在哪个位置都可以,放完后,需要在配置文件中写一下说这个文件放在了哪里
这样我的程序就可以运算出来那个绝对路径 学javaweb时就会学到这种方式 得到绝对路径
在javaweb中,你的东西肯定是放在你的web项目里边,得到web项目在硬盘上的对应的具体目录,getRealPath;而你的配置文件肯定是在web项目文件内部,你得到了web项目绝对位置,再拼上你内部那个位置就拼到了一个真实完整的位置,得到你总的目录在硬盘上对应的绝对位置
类加载器:
每一个.class文件在使用时都要加载到内存中来,就是说一定要有个东西,将硬盘上的.class文件搞到内存里面来,
既然类加载器能够加载class文件 那是不是也能够加载普通文件呢
InputString ips = eefiectTest2.class.getClassLoader().getResourceAsStream("cn/itcast/day1/config.properties");//在classpath指定的目录下,逐一的去查找你要加载的那个文件,
InputString ips = RefiectTest2.class.getResourceAsStream("config.properties");