关于一些基础的Java问题的解答(七)
31. 反射的作用与原理
- import java.lang.reflect.Field;
- import java.lang.reflect.Method;
- class A {
- private int varA;
- public void myPublicA() {
- System.out.println("I am public in A !");
- };
- private void myPrivateA() {
- System.out.println("I am private in A !");
- };
- }
- class B extends A {
- public int varB;
- public void myPublicB(){};
- }
- public class Main {
- public static void main(String[] args) throws Exception {
- B b = new B();
- // 子类方法
- Method methods[] = b.getClass().getMethods();
- for (Method method : methods)
- System.out.println(method);
- System.out.println("");
- // 子类变量
- Field fields[] = b.getClass().getFields();
- for (Field field : fields)
- System.out.println(field);
- // 基类
- System.out.println("\n" + b.getClass().getSuperclass() + "\n");
- // 基类private方法也不能避免
- Class superClass = b.getClass().getSuperclass();
- methods = superClass.getDeclaredMethods();
- for (Method method : methods) {
- System.out.println(method);
- method.setAccessible(true);
- // 实例化A来调用private方法!
- method.invoke(superClass.newInstance(), null);
- }
- }
- }
在上面的例子中,我们用一个子类B,通过反射找到了他的数据域、与方法,还找到了他的基类A。更甚者,我们实例化了基类A,还调用了A里面的所有方法,甚至是private方法。从以上的例子相信大家都感受到了反射的威力了,运用使用class对象和反射提供的方法我们可以轻易的获取一个类的所有信息,包括被封装隐藏起来的信息,不仅如此我们还可以调用获取的信息来构造实例和调用方法。以上的展示只是反射的冰山一角,反射在动态代理和调用隐藏API等黑科技方面还发挥着重要的作用,博主在此不做深入探讨。
32. 泛型常用特点,List<String>能否转为List<Object>
- List<String> listString = new ArrayList<>();
- // error : Type mismatch: cannot convert from List<String> to List<Object>
- List<Object> listObject = listString;
- // it's ok !
- List<? extends Object> listExtendsObject = listString;
- // error : The method add(capture#1-of ? extends Object) in the type List<capture#1-of ? extends Object> is not applicable for the
- // arguments (String)
- listExtendsObject.add("string");
- // it's ok !
- listExtendsObject.add(null);
- // it's ok !
- Object object = listExtendsObject.get(0);
接下来讲讲泛型常用的特点,利用泛型我们可以实现以下内容:
1.带参数类型的类,泛型类
- class A<T,S> {
- }
2.带参数类型的方法,泛型方法
- public <T> void f(T x) {
- }
使用泛型方法时通常不必指明参数类型,编译器会为我们找出具体类型,这称为类型参数推断。
3.关键字
- class A {}
- class B extends A {}
- class C {};
- //代表了T为A或A的子类
- class D <T extends A> {};
- public class Main {
- public static void main(String[] args) {
- D<A> a;
- D<B> b;
- // no work!
- D<C> c;
- }
- }
super与extends相反,把类型参数限制为某个类的父类。(此处博主研究不够深入,故对super关键字不够了解,在此不深入讨论)
4.擦除
- import java.util.Arrays;
- class A {}
- class B extends A {}
- class C extends B {};
- class D <T> {
- T t;
- D(T t) {
- this.t = t;
- }
- public void f() {
- System.out.println(Arrays.toString(this.getClass().getTypeParameters()));
- }
- };
- public class Main {
- public static void main(String[] args) {
- D<A> a = new D<A>(new A());
- D<B> b = new D<B>(new B());
- D<C> c = new D<C>(new C());
- a.f();
- b.f();
- c.f();
- }
- }
D中的f方法通过获取D的Class类来获取其类型信息,其打印的结果如下:
并不是我们传入的参数A、B、C,真是太失望了。这就是Java泛型擦除的特点,残酷的现实告诉我们在泛型代码的内部,我们无法获得任何有关泛型的参数类型信息,擦除会把类的类型信息给擦除到它的边界(如果有多个边界会擦除到第一个)。也就是说,对于上面例子中的T,我们只能把其当做Object类来处理(Object类为所有类的父类)。擦除使得所有与类型信息相关的操作都无法在泛型代码中进行,extends会稍微改善一点这种情况:
- class A {
- public void fa() {};
- }
- class C <T> { // 擦除到Object
- T t;
- public void f(Object a) {
- if (a instanceof T) {} // error,不知道具体的类型信息
- T var = new T(); // error,不知道该类型是否有默认构造函数
- T[] array = new T[1]; // error
- t.fa(); // error
- }
- };
- class D <T extends A> { // 擦除到A
- T t;
- public void f(Object a) {
- t.fa(); // this works
- }
- };
33. 解析XML的几种方式的原理与特点:DOM、SAX、PULL
1.DOM
- 使用DocumentBuilderFactory.newInstance方法获取DOM工厂实例
- 使用工厂的newDocumentBuilder方法获取builder
- 使用builder的parse方法解析xml获取生成的Document对象
- 使用Document的getDocumentElement方法获取根节点
- 调用根节点的getChildNodes方法遍历子节点
2.SAX
使用SAX解析主要步骤如下:
- 调用SAXParserFactory.newInstance获取SAX工厂实例
- 调用工厂的newSAXParser方法获取解析器
- 调用解析器的getXMLReader获取事件源reader
- 调用setContentHandler方法为事件源reader设置处理器
- 调用parse方法开始解析数据
3.PULL
- 读取到xml的声明返回 START_DOCUMENT
- 读取到xml的结束返回 END_DOCUMENT
- 读取到xml的开始标签返回 START_TAG
- 读取到xml的结束标签返回 END_TAG
- 读取到xml的文本返回 TEXT
- 使用XmlPullParserFactory.newInstance方法获取Pull工厂
- 调用工厂newPullParser方法返回解析器
- 使用解析器setInput方法设置解析文件
- 调用next方法解析下一行,调用getEventType方法获取当前的解析情况
- public static void main(String[] args) throws Exception {
- // 桌面的xml文件,文件内容如下
- // <all name="testData">
- // <item first="1" second="A" third="一"/>
- // <item first="2" second="B" third="二"/>
- // <item first="3" second="C" third="三"/>
- // <item first="4" second="D" third="四"/>
- // <item first="5" second="E" third="五"/>
- // </all>
- File file = new File("C:/Users/Administrator/Desktop/test.xml");
- // 文件流
- FileInputStream fis = new FileInputStream(file);
- {
- // DOM解析xml
- System.out.println("DOM:");
- // 获取DOM工厂实例
- DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
- // 生成builder
- DocumentBuilder builder = factory.newDocumentBuilder();
- // 解析文件
- Document document = builder.parse(file);
- // 获取根节点
- Element element = document.getDocumentElement();
- System.out.println(element.getTagName() + " " + element.getAttribute("name"));
- // 获取子节点列表
- NodeList nodeList = element.getChildNodes();
- for (int i = 0; i < nodeList.getLength(); i++) {
- Node node = nodeList.item(i);
- // 节点类型为元素节点
- if (node.getNodeType() == Node.ELEMENT_NODE) {
- Element child = (Element) node;
- // 输出标签名和元素内容
- System.out.println(child.getTagName() + " " + child.getAttribute("first") + " "
- + child.getAttribute("second") + " " + child.getAttribute("third"));
- }
- }
- }
- System.out.println(""); // empty line
- {
- // SAX解析xml
- System.out.println("SAX:");
- // 获取SAX工厂实例
- SAXParserFactory factory = SAXParserFactory.newInstance();
- // 获取SAX解析器
- SAXParser parser = factory.newSAXParser();
- // 获取reader
- XMLReader reader = parser.getXMLReader();
- // 设置解析源和处理器
- reader.setContentHandler(new MySAXHandler()); // 在parse之前设置
- reader.parse(new InputSource(fis));
- }
- }
- // 自定义SAX处理器
- static class MySAXHandler extends DefaultHandler {
- @Override
- public void startDocument() throws SAXException {
- // 解析文档开始时调用
- super.startDocument();
- }
- @Override
- public void startElement(String uri, String localName, String qName, Attributes attributes)
- throws SAXException {
- // 解析元素开始时调用
- // 打印元素名
- System.out.print(qName);
- // 打印元素属性
- for (int i = 0; i < attributes.getLength(); i++)
- System.out.print(" " + attributes.getValue(i));
- System.out.println("");
- super.startElement(uri, localName, qName, attributes);
- }
- @Override
- public void endElement(String uri, String localName, String qName) throws SAXException {
- // 解析元素结束时调用
- super.endElement(uri, localName, qName);
- }
- @Override
- public void endDocument() throws SAXException {
- // 解析文档结束时调用
- super.endDocument();
- }
- }
Pull(此为Android解析中国天气网省份信息xml文件的例子):
- XmlPullParserFactory factory = XmlPullParserFactory
- .newInstance();
- XmlPullParser xmlPullParser = factory.newPullParser();
- // use gb2312 encode
- ByteArrayInputStream is = new ByteArrayInputStream(
- response.getBytes("GB2312"));
- xmlPullParser.setInput(is, "GB2312");
- int eventType = xmlPullParser.getEventType();
- String provinceName = "";
- String provinceCode = "";
- while (eventType != XmlPullParser.END_DOCUMENT) {
- String nodeName = xmlPullParser.getName();
- switch (eventType) {
- // start parse node
- case XmlPullParser.START_TAG:
- if ("city".equals(nodeName)) {
- provinceName = xmlPullParser.getAttributeValue("",
- "quName");
- provinceCode = xmlPullParser.getAttributeValue("",
- "pyName");
- Province province = new Province();
- province.setProvinceName(provinceName);
- province.setProvinceCode(provinceCode);
- coolWeatherDB.saveProvince(province);
- }
- break;
- default:
- break;
- }
- eventType = xmlPullParser.next();
本人对三种解析方法的总结如下:
- 需要解析小的xml文件,需要重复解析xml文件或需要对xml文件中的节点进行删除修改排序等操作:使用DOM
- 需要解析较大的xml文件,只需要解析一次的xml文件:使用SAX或Pull
- 只需要手动解析部分的xml文件:使用Pull
34. Java与C++对比
Java是由C++发展而来的,保留了C++的大部分内容,其编程方式类似于C++,但是摒弃了C++的诸多不合理之处,Java是纯面向对象的编程语言。Java和C++的区别主要如下:
1.都是类与对象
在Java中,一切的组件都是类与对象,没有单独的函数、方法与全局变量。C++中由于可以使用C代码,故C++中类对象与单独的函数方法共存。
2.多重继承
在C++中类可以多重继承,但这有可能会引起菱形问题。而在Java中类不允许多重继承,一个类只能继承一个基类,但可以实现多个接口,避免了菱形问题的产生。
3.操作符重载
C++允许重载操作符,而Java不允许。
4.数据类型大小
在C++中,不同的平台上,编译器对基本数据类型分别分配不同的字节数,导致了代码数据的不可移植性。在Java中,采用基于IEEE标准的数据类型,无论任何硬件平台上对数据类型的位数分配总是固定的。(然而boolean基本类型要看JVM的实现)
5.内存管理
C++需要程序员显式地声明和释放内存。Java中有垃圾回收器,会在程序内存不足或空闲之时在后台自行回收不再使用的内存,不需要程序员管理。
6.指针
指针是C++中最灵活也最容易出错的数据类型。Java中为了简单安全去掉了指针类型。
7.类型转换
C++中,会出现数据类型的隐含转换,涉及到自动强制类型转换。Java中系统要对对象的处理进行严格的相容性检查,防止不安全的转换。如果需要,必须由程序显式进行强制类型转换。(如int类型不能直接转换为boolean类型)
8.方法绑定
C++默认的方法绑定为静态绑定,如果要使用动态绑定实现多态需要用到关键字virtual。Java默认的方法绑定为动态绑定,只有final方法和static方法为静态绑定。
目前博主就想到这么多,还有的以后再补充。
35. Java1.5、1.7与1.8新特性
JDK1.5:
- 自动装箱与拆箱:基本类型与包装类型自动互换
- 枚举类型的引入
- 静态导入:import static,可直接使用静态变量与方法
- 可变参数类型
- 泛型
- for-each循环
JDK1.7:
- switch允许传入字符串
- 泛型实例化类型自动推断:List<String> tempList = new ArrayList<>()
- 对集合的支持,创建List / Set / Map 时写法更简单了,如:List< String> list = ["item"],String item = list[0],Set< String > set = {"item"}等
- 允许在数字中使用下划线
- 二进制符号加入,可用作二进制字符前加上 0b 来创建一个二进制类型:int binary = 0b1001_1001
- 一个catch里捕捉多个异常类型,‘|’分隔
JDK1.8:
- 允许为接口添加默认方法,又称为拓展方法,使用关键字default实现
- Lambda 表达式
- Date API
- 多重注解
36. JNI的使用
- public class Main {
- public static void main(String[] args) throws Exception {
- // 动态库名字,windows平台自动拓展成makeStr_jni.dll
- System.loadLibrary("makeStr_jni");
- // 打印字符串
- printString("Java World!");
- }
- // native关键字表示为本地方法
- public static native void printString(String str);
- }
我们在Java中使用JNI接口只需要两步:
- 使用native关键字声明某方法为本地方法
- 使用System.loadLibrary加载由C/C++编写成的动态链接库(我们只需要写出库名字即可,Java会根据平台补充库的全名windows:dll,linux:so)
- /* DO NOT EDIT THIS FILE - it is machine generated */
- #include "jni.h"
- /* Header for class Main */
- #ifndef _Included_Main
- #define _Included_Main
- #ifdef __cplusplus
- extern "C" {
- #endif
- /*
- * Class: Main 方法所在的类
- * Method: printString 方法名
- * Signature: (Ljava/lang/String;)V 签名
- */
- JNIEXPORT void JNICALL Java_Main_printString
- (JNIEnv *, jclass, jstring);
- #ifdef __cplusplus
- }
- #endif
- #endif
可以看到我们在Java层定义的printString方法对应成了Native层的Java_Main_printString方法,方法上面有javah给我们生成的注释,它提供了以下信息:
- Class:方法所属于的类
- Method:方法的名称
- Signature:方法的签名。签名是由于Java中的方法允许重载,仅仅通过类与名称并不能确定该Native方法所对应的Java方法,因此JNI技术中就将参数类型和返回值的组合作为了一个函数的签名信息,有了签名和函数名我们才能顺利找到Javac层中对应的函数
- #include<iostream>
- #include"_Main.h"
- using namespace std;
- /*
- * JNIEnv : env JNI环境,一个提供JNI系统函数的结构体
- * jclass : clazz 代表Java层的Class对象,由于printString方法是一个静态方法,故传入Class对象
- * jstring : s 代表Java层的String对象,表示传入的参数
- */
- JNIEXPORT void JNICALL Java_Main_printString
- (JNIEnv * env, jclass clazz, jstring s) {
- jboolean iscopy;
- // 通过jstring对象生成本地字符串
- const char *charData = env->GetStringUTFChars(s, &iscopy);
- // 打印字符串
- cout << "A message from Native World: " << charData << endl;
- // 释放资源
- env->ReleaseStringUTFChars(s, charData);
- }
以上代码相信注释已解释的非常清楚,故此处不再赘述。值得一提的是在Native层中有多种与Java层中相对应的数据结构,如:jclass代表Class对象,jstring代表String对象,jboolean代表boolean基本类型等。有兴趣的童鞋可以自行去了解下。
关于一些基础的Java问题的解答(七)的更多相关文章
- 关于一些基础的Java问题的解答(一)
学习一门语言基础是非常重要的,因此本文总结了一些常见的Java基础问题的解答,希望可以帮到大家. 1. 九种基本数据类型的大小,以及他们的封装类. 9种基本数据类型 基本类型 包装类型 大小 bool ...
- 关于一些基础的Java问题的解答(六)
26. ThreadPool用法与优势 ThreadPool即线程池,它是JDK1.5引入的Concurrent包中用于处理并发编程的工具.使用线程池有如下好处: 降低资源消耗:通过重复利用已创建的线 ...
- 关于一些基础的Java问题的解答(四)
16. Java面向对象的三个特征与含义 java中的面向对象的三大基本特征分别是:封装.继承.多态: 封装:把过程和数据包围起来,对数据的访问只能通过已定义的界面,主要是方便类的修改 继承:对象的一 ...
- 关于一些基础的Java问题的解答(五)
21. 实现多线程的两种方法:Thread与Runable 在Java中实现多线程编程有以下几个方法: 1.继承Thread类,重写run方法 public class Test { public s ...
- 关于一些基础的Java问题的解答(三)
11. HashMap和ConcurrentHashMap的区别 从JDK1.2起,就有了HashMap,正如上一个问题所提到的,HashMap与HashTable不同,不是线程安全的,因此多线程 ...
- 关于一些基础的Java问题的解答(二)
6. Hashcode的作用 官方对于hashCode的解释如下: Whenever it is invoked on the same object more than once during an ...
- 黑马程序员:Java基础总结----java注解
黑马程序员:Java基础总结 java注解 ASP.Net+Android+IO开发 . .Net培训 .期待与您交流! java注解 lang包中的基本注解 @SuppressWarnings ...
- java面试题—精选30道Java笔试题解答(二)
摘要: java面试题-精选30道Java笔试题解答(二) 19. 下面程序能正常运行吗() public class NULL { public static void haha(){ System ...
- Java基础:Java的四种引用
在Java基础:java虚拟机(JVM)中,我们提到了Java的四种引用.包括:强引用,软引用,弱引用,虚引用.这篇博客将详细的讲解一下这四种引用. 1. 强引用 2. 软引用 3. 弱引用 4. 虚 ...
随机推荐
- Mego开发文档 - 事务
事务 事务允许以原子方式处理多个数据库操作.如果事务已提交,则所有操作都已成功应用于数据库.如果事务回滚,则没有任何操作应用于数据库. 默认行为 默认情况下,如果数据库提供程序支持事务,则单次的提交操 ...
- sts 和 lombok
1.安装lombok.jar到sts.exe所在目录 如果是eclipse,需要放到eclipse.exe所在目录,同理myeclipse. 2.修改sts.ini配置使用lombok 如果是ecli ...
- python 爬取百度翻译进行中英互译
感谢RoyFans 他的博客地址http://www.cnblogs.com/royfans/p/7417914.html import requests def py(): url = 'http: ...
- linux centos6.8 下安装mysql 步骤
安装环境:vmware12.centos6.8.centos中配置阿里云数据元 1.下载mysql 运行: sudo yum -y install mysql-server 如果下载失败,可以卸载重新 ...
- MongoDB GridFS 存储大文件
我们经常会遇到这样的场景:上传/下载文件. 有两种思路可以解决这个问题: (1)将文件存储在服务器的文件系统中: (2)将文件存储在数据库中. 如果我们选择(2),那么我们可以使用MongoDB Gr ...
- Python系列-python函数
函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段. 函数能提高应用的模块性,和代码的重复利用率.你已经知道Python提供了许多内建函数,比如print().但你也可以自己创建函数,这 ...
- Stanford依存句法关系解释
ROOT:要处理文本的语句 IP:简单从句 NP:名词短语 VP:动词短语 PU:断句符,通常是句号.问号.感叹号等标点符号 LCP:方位词短语 PP:介词短语 CP:由'的'构成的表示修饰性关系的短 ...
- python3全栈开发-面向对象、面向过程
一. 什么是面向对象的程序设计及为什么要有它 1.面向过程 面向过程的程序设计:核心是过程二字,过程指的是解决问题的步骤,即先干什么再干什么......面向过程的设计就好比精心设计好一条流水线,是一种 ...
- zoj 3981 Balloon Robot
https://vjudge.net/problem/ZOJ-3981 题意: 有m个座位,其中n个队伍坐在这些位置上,一个队伍一个座位.当一个队A了题之后,他们们会得到气球,假设他们在a时刻A题,但 ...
- $rootscope说明
scope是AngularJS中的作用域(其实就是存储数据的地方),很类似JavaScript的原型链 .搜索的时候,优先找自己的scope,如果没有找到就沿着作用域链向上搜索,直至到达根作用域roo ...