1. XML
Xml格式的文件在接下来的框架学习尤为重要,比如在框架中的配置文件大多都是用的xml格式的文件来配置的,所以xml格式的文件创建与解析特别重要。
1.1 XML概念
XML(eXtensible Markup Language)是一种可扩展标识语言,是一种简单的数据存储语言,使用一系列简单的标记描述数据。目前推荐遵循的是W3C组织于2000年发布的XML1.0版本。
1.2 XML语法规则
①、声明标签定在首行:<?xml version=“1.0” encoding=“utf-8|gb2312”?>
②、文档有且只有一个根元素
③、非空元素都必须有一对标签:起始/关闭标签
④、空元素的标签中同时表示起始和结束标签</>
⑤、大小写敏感
⑥、标签必须正确地嵌套
⑦、元素的属性必须有属性值且属性值必须加引号(单引或双引都可以)
⑧、在 XML 中,应使用实体引用来代替特殊字符
1.3 实体引用
< 小于号< 【必须】
> 大于号>
& 和号& 【必须】
' 单引号‘
" 双引号”
1.4 元素命名规则
名称可以含字母、数字以及其他的字符
名称不能以数字或者标点符号开始
名称不能以字符 “xml”(或者 XML、Xml)开始
名称不能包含空格
可使用任何名称,没有保留的字词
1.5 XML的特点
XML 仅仅是纯文本
XML 的设计宗旨是传输数据,而非显示数据
XML 是独立于软件和硬件的信息传输工具
XML 标签没有被预定义,需要自行定义标签
1.6 Dom4j概念
dom4j是一个Java的XML API,是jdom的升级品,用来读写XML文件的。dom4j是一个十分优秀javaXML API,具有性能优异、功能强大和极其易使用的特点,它的性能超过sun公司官方的dom技术,同时它也是一个开放源代码的软件,可以在SourceForge上找到它。在IBM developerWorks上面还可以找到一篇文章,对主流的Java XML API进行的性能、功能和易用性的评测,所以可以知道dom4j无论在哪个方面都是非常出色的。如今可以看到越来越多的Java软件都在使用dom4j来读写XML,特别值得一提的是连Sun的JAXM也在用dom4j。这已经是必须使用的jar包, Hibernate也用它来读写配置文件。
1.7 XML文档示例
<?xml version="1.0" encoding="UTF-8"?>
<books>
<!--This is a test for dom4j, holen, 2004.9.11-->
<book show="yes">
<title>Dom4j Tutorials</title>
</book>
<book show="yes">
<title>Lucene Studing</title>
</book>
<book show="no">
<title>Lucene in Action</title>
</book>
<owner>O'Reilly</owner>
</books>
1.8 解析XML文档
解析/创建都是基于dom4j实现
public static void main(String[] args) throws DocumentException {
File file = new File("hello.xml");
SAXReader sax = new SAXReader();
Document doc = sax.read(file);
Element rootElement = doc.getRootElement();
String name = rootElement.getName();
System.out.println("根节点="+name);
//判断是否是根节点
System.out.println(rootElement.isRootElement());
Iterator it = rootElement.elementIterator();
while(it.hasNext()){
Element ele = (Element)it.next();
String eleName = ele.getName();
if(eleName.equals("book")){
Attribute attribute = ele.attribute(0);
System.out.println(attribute.getName()+"="+attribute.getValue());
Iterator it2 = ele.elementIterator();
while(it2.hasNext()){
Element ele2 = (Element)it2.next();
System.out.println(ele2.getName()+"="+ele2.getText());
}
}else{
System.out.println(ele.getText());
}
}
}
1.9 创建XML文档
public static void main(String[] args) throws IOException, XMLStreamException {
Document document = DocumentHelper.createDocument();
Element booksElement = document.addElement("books");
booksElement.addComment("This is a test for dom4j, holen, 2004.9.11");
Element bookElement = booksElement.addElement("book");
bookElement.addAttribute("show","yes");
Element titleElement = bookElement.addElement("title");
titleElement.setText("Dom4j Tutorials");
/** 类似的完成后两个book */
bookElement = booksElement.addElement("book");
bookElement.addAttribute("show","yes");
titleElement = bookElement.addElement("title");
titleElement.setText("Lucene Studing");
bookElement = booksElement.addElement("book");
bookElement.addAttribute("show","no");
titleElement = bookElement.addElement("title");
titleElement.setText("Lucene in Action");
/** 加入owner节点 */
Element ownerElement = booksElement.addElement("owner");
ownerElement.setText("O'Reilly");
//用于格式化xml内容和设置头部标签
OutputFormat format = OutputFormat.createPrettyPrint();
//设置xml文档的编码为utf-8
format.setEncoding("utf-8");
Writer out = new FileWriter("hello2.xml");
XMLWriter writer = new XMLWriter(out,format);
writer.write(document);
writer.close();
}
2. 反射
2.1 定义
1、对于给定的一个类(Class)对象,可以获得这个类(Class)对象的所有属性和方法;
2、对于给定的一个对象(new XXXClassName<? extends Object>),都能够调用它的任意一
个属性和方法。
这种动态获取类的内容以及动态调用对象的方法和获取属性的机制,就叫做Java反射机制
2.2 反射初识
public class User {
public void eat(){
System.out.println("---eat---");
}
}
public class TestReflect{
public static void main(String[] args) throws Exception {
Class<User> clazz = User.class;
//获取类对象对应的属性和方法
System.out.println(clazz.getName());
System.out.println(clazz.getPackage());
System.out.println(clazz.getSuperclass());
System.out.println(clazz.getClassLoader());
//获取一个对象实例
User user = clazz.newInstance();
Method method = clazz.getDeclaredMethod("eat");
method.invoke(user);
}
2.3 为什么使用反射
实例化一个User()对象,不使用反射,如果想再实例化其他对象比如new Person(),那么
就需要修改源代码,并重新编译。使用反射后就不需要修改源代码只需要灵活改动类描述就
可以了,不需要重新再编译。 如下代码所示
//类描述,可根据需要实例化的描述而改动
String className = "com.tledu.pojo.User";
//使用反射中API创建对象
Class.forName(className).newInstance();
2.4 反射的优缺点
优点:
增加程序的灵活性,避免固有逻辑写死到程序中
代码相对简洁,可以提高程序的复用性
缺点:
相比于直接调用反射有比较大的性能销毁
内部暴露和安全隐患
2.4.1 灵活性测试
public interface Ball {
void playBall();
}
public class BasketBall implements Ball{
@Override
public void playBall() {
System.out.println("打篮球");
}
}
public class FootBall implements Ball{
@Override
public void playBall() {
System.out.println("踢足球");
}
}
//----------------------------
public static void main(String[] args) {
//System.out.println(getInstanceByKey("basket"));
System.out.println(getInstanceReflectByKey("FootBall"));
}
public static Ball getInstanceByKey(String key) {
if("foot".equals(key)){
return new FootBall();
}
if("basket".equals(key)){
return new BasketBall();
}
return null;
}
如果我们想在添加一个乒乓球那么除了创建一个乒乓球类,还需要在方法中加入获取乒乓球对应的判断和乒乓球创建对象的返回,很显然不够灵活出现了写死代码的情况。
public static Ball getInstanceReflectByKey(String key) {
String basePackage = "com.tledu.mjw";
Ball ball = null;
try {
Class clazz = Class.forName(basePackage + "." + key);
ball = (Ball)clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return ball;
}
使用反射的方式获取对象,可以看出代码灵活了很多而且不需要改动创建返回对象的源码。
2.4.2 性能测试
public static void main(String[] args) {
//System.out.println(getInstanceByKey("basket"));
//System.out.println(getInstanceReflectByKey("FootBall"));
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++){
getInstanceByKey("basket");
//getInstanceReflectByKey("FootBall");
}
long end = System.currentTimeMillis();
System.out.println("耗时:" + (end - start));
}
2.5 反射的操作
2.5.1 获取类对象的四种方式
//获取类对象的四种方式
Class<User> clazz1 = User.class;
Class<?> clazz2 = Class.forName("com.tledu.mjw.User");
Class<? extends User> clazz3 = new User().getClass();
Class<?> clazz4 = Demo3.class.getClassLoader().loadClass("com.tledu.mjw.User");
2.5.2 类中基本信息的获取
System.out.println(clazz1.getClassLoader());
System.out.println(clazz1.getSuperclass());
System.out.println(clazz1.getPackage());
System.out.println(clazz1.getModifiers()); //获取类的修饰符
System.out.println(clazz1.getName());
System.out.println(clazz1.getSimpleName());
System.out.println(clazz1.getInterfaces().length);//获取类实现的所有接口
System.out.println(clazz1.getAnnotations().length);
2.5.3 类中字段的操作
创建User类继承Person类添加相应属性
public class Person {
public String idCard;
private String userName;
}
public class User extends Person{
private String name;
public String sex;
public static String address;
public void eat(){
System.out.println("---eat---");
}
}
2.5.4 测试方法调用
public static void main(String[] args) throws Exception {
Class<User> userClass = User.class;
User user = userClass.newInstance();
//getFields()获取的是本类及父类中的公共属性
Field[] fields = userClass.getFields();
for (Field field : fields) {
System.out.println(field.getModifiers() + " " + field.getName());
}
System.out.println("---------------");
//getDeclaredFields()获取本类中的所有属性
Field[] fields2 = userClass.getDeclaredFields();
for (Field field : fields2) {
System.out.println(field.getModifiers() + " " + field.getName());
}
System.out.println("---------------");
//获取name字段对应的field
Field nameField = userClass.getDeclaredField("name");
//如果需要修改私有属性信息那么我们要放开权限
nameField.setAccessible(true);
nameField.set(user,"天亮教育");
System.out.println(nameField.get(user));
//System.out.println(user.getName());
System.out.println("---------------");
//如何对静态的属性赋值
Field addressField = userClass.getDeclaredField("address");
addressField.set(null,"Jack");
System.out.println(addressField.get(null));
//System.out.println(User.address);
}
2.5.5 类中方法的操作
在User类中将eat方法改为私有的,并添加静态方法say
private void eat(){
System.out.println("---eat---");
}
public static void say(String msg){
System.out.println(msg);
}
在Person类中添加共有的fn1()方法和私有的fn2()方法
public void fn1(){}
private void fn2(){}
2.5.6 测试方法调用
public static void main(String[] args) throws Exception {
Class<User> userClass = User.class;
User user = userClass.newInstance();
//获取本类及父类中的公共方法
Method[] methods1 = userClass.getMethods();
for (Method method : methods1) {
System.out.println(method.getModifiers() + " " + method.getName());
}
System.out.println("--------------");
//获取本类中所有的方法包括私有
Method[] methods2 = userClass.getDeclaredMethods();
for (Method method : methods2) {
System.out.println(method.getModifiers() + " " + method.getName());
}
System.out.println("--------------");
//调用方法执行
Method eatMethod = userClass.getDeclaredMethod("eat");
//调用私有方法时要先开放权限
eatMethod.setAccessible(true);
eatMethod.invoke(user);
System.out.println("--------------");
//调用静态的方法
Method sayMethod = userClass.getDeclaredMethod("say",String.class);
sayMethod.invoke(null,"你好");
}
2.5.7 类中构造器的操作
在User中添加如下构造方法
public User() {
}
public User(String name) {
this.name = name;
}
private User(String name,String sex) {
this.name = name;
this.sex = sex;
}
通过反射操作User中的构造方法
public static void main(String[] args) throws Exception {
Class<User> userClass = User.class;
//获取公共的构造器
Constructor<?>[] constructors = userClass.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor.getModifiers() + " " + constructor.getName());
}
System.out.println("------------------");
//获取所有的构造器
Constructor<?>[] declaredConstructors = userClass.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor.getModifiers() + " " + declaredConstructor.getName());
}
//1、直接通过newInstance创建对象
User user = userClass.newInstance();
//2、获取对应的Constructor对象获取实例
Constructor<User> constructor = userClass.getDeclaredConstructor(String.class, String.class);
//操作私有的构造器要先打开权限
constructor.setAccessible(true);
User user1 = constructor.newInstance("乔森", "男");
}
2.5.8 反射破局单例
单例模式
public class PersonSingle {
private static PersonSingle instance = null;
private PersonSingle(){}
public static PersonSingle getInstance(){
if(instance == null){
instance = new PersonSingle();
}
return instance;
}
}
测试单例、通过反射可以调用私有构造
public static void main(String[] args) throws Exception {
PersonSingle instance1 = PersonSingle.getInstance();
PersonSingle instance2 = PersonSingle.getInstance();
PersonSingle instance3 = PersonSingle.getInstance();
System.out.println(instance1);
System.out.println(instance2);
System.out.println(instance3);
System.out.println("---------------------");
Class<PersonSingle> personSingleClass = PersonSingle.class;
Constructor<PersonSingle> declaredConstructor = personSingleClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
System.out.println(declaredConstructor.newInstance());
}
改进单例如下
private PersonSingle(){
if(instance != null){
throw new RuntimeException("实例已经存在不允许再创建")
}
}
2.5.9 反射使用场景
1、jdbc封装
2、SpringIOC
3、JdbcTemplate
4、Mybatis