Java学习记录(记录一部分Java和C++区别和Java里一些比较重要的概念)--稍后会转到新浪博客

Java

1. 单根继承

Java里是单根继承,所有类都继承至Object -- 单根继承在泛型编程里的通用容器起很大作用,因为都继承至同一个基类,通过向上类型转换我能可以只存储Object索引即可。同时单根继承在GC里也起了很大作用,通过Object类型引用来管理所有对象,把必要的垃圾回收相关的工作放在基类中去实现。

C++不是单根继承,C++里是通过STL(标准模板库)的使用来实现泛型编程里通用容器的概念

Note:

(以下引用至维基百科 -- 泛型)

"

泛型的定義主要有以下兩種:

1. 在程序编码中一些包含类型参数的类型,也就是说泛型的参数只可以代表类,不能代表个别对象。(这是当今较常见的定义)

2. 在程序編碼中一些包含參數的。其參數可以代表类或对象等等。(現在人們大多把這稱作模板)

"

泛型支持静态多态和动态多态:(一下转载至 --http://www.kuqin.com/language/20080615/9579.html

std::vector<Shape*> v;
… // fill v
std::for_each(v.begin(), v.end(), std::mem_fun(&Shape::draw));
这里,v里面到底将来会存放什么类型的Shape,编译期无法知道,因而必须求助于动态多态。另一方面,编译器倒的确知道它们都继承自Shape,利用这仅有的静态类型信息,我们使用了泛型算法std::for_each和泛型容器std::vector。这里尤其值得注意的是for_each的静态多态行为:for_each只有一份模板实现,然而根据传给它的第三个参数(本例中是std::mem_fun(&Shape::draw))的不同,for_each的行为也不同(这里最终被for_each调用的是Shape::draw,但实际上你可以包装任何函数,只要这个函数接受一个Shape*型的参数),for_each这种“行为不同”是发生在编译期的,所以是静态多态。
这里提到一个模板元编程(TMP -- Template metaprogramming)的概念

核心思想: (-- 一下引用至:http://itlab.idcquan.com/c/example/961585.html

模板元编程(Template metaprogramming,简称TMP)是编译器内执行的程序,编译器读入template,编译输出的结果再与其他源码一起经过普通编译过程生成目标文件。通俗来说,普通运行程序是编译器生成的机器码,由处理器解释执行得到结果,TMP则是编译器实例化template过程中得到结果。TMP已被证明是图灵完备的机器,不过模板实例化通常需要消耗巨大的编译器资源,而且难以追踪错误,没有合适的调试器,所以在实际开发中很少使用。

更多关于类型本质和函数式实现可参考下文 -- http://coolshell.cn/articles/10169.html


个人理解:(结合http://www.kuqin.com/language/20080615/9579.htmlhttp://coolshell.cn/articles/10169.html所讲内容)

泛型是消除类型不同的一层抽象

结构一致性:

只要该类型符合调用相同的类型规范(方法调用等),就能支持泛化(比如C++模板里的参数类型泛化,所有内置类型支持相同的类型规范(e.g. >,<,= ......))

名字一致性:

基于接口继承的面向对象多态,只要都继承至相同的接口类,就能被泛化(Java里的单一继承在这里适用,这里多态起了关键作用)


2.垃圾回收器(GC) -- 参考官网 -- http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html

Java拥有自动的垃圾回收机制,而C++在这一点上是交给了程序员去决定什么时候释放对象(让程序员去管理内存)

Java的垃圾回收是针对堆上的memory使用,通过观察堆上object是否还在使用(有没有引用)来决定是否回收

Java GC工作模式:(这里提一下cocos2d-x里面的GC是通过索引计数实现的)

1. Marking (标记 -- 清扫)

标记哪些object有被使用(索引),哪些没有

清扫未被使用(索引)的object

深入优化学习了解(Eden Generation,Old Generatiion, Permernent Generation......)见Describe Garbage Collection & The Generational Garbage Collection Process --  http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html

可视化GC可使用VisualVM

(后面会讲到索引和GC的关系)


3.索引(Reference)

Java里一切都被视为对象,Java里没有指针的概念,但是reference一定程度上和C++的指针很类似,后面讲解

我们先看看Java里什么时候是引用传递,什么时候是值传递 (参考 -- http://6924918.blog.51cto.com/6914918/1283761

看下面的例子:

class TestClass{
public TestClass() {
// TODO Auto-generated constructor stub
member = 1;
}
public int member;
};

public class ReferenceStudy {

public static void CallB(String vb)
{
vb = vb.toUpperCase();
System.out.println("vb = " + vb);
}

public static void ChangeMemberValue(TestClass testclass)
{
testclass.member = 2;
}

    public static void main(String[] args){
        String a = "testA";
        System.out.println("a = " + a);
        CallB(a);
        System.out.println("Final a = " + a);
        
        TestClass testA = new TestClass();
        System.out.println("testA.member = " + testA.member);
        ChangeMemberValue(testA);
        System.out.println("Later testA.member = " + testA.member);
    }
}

输出为:

a = testA
vb = TESTA
Final a = testA
testA.member = 1
Later testA.member = 2

从上面可以看出,在传递基本类型的时候,是值传递,在传递类对象的时候是引用传递

Java引用分类: 参考 -- http://blog.csdn.net/mazhimazh/article/details/19752475

1. 强引用(StrongReference) -- 强引用需要我们显示的设置为null或者超出生命周期范围,gc判断不再有任何的强引用的时候才会回收该内存

2. 软引用(SoftReference) -- 软引用只有在内存不足的时候,gc判断该对象没有强引用且都是软引用的时候会回收该内存

3. 弱引用(WeaKReference) -- 弱引用,gc判断一个对象只有弱引用的时候无论内存足够不足够都会回收该内存

弱引用实例:

        String a=new String("a");      
        WeakReference<String> aRef = new WeakReference<String>(a);  
        System.out.println("aRef = " +aRef.get());
        a=null;
        System.gc();
        System.out.println("Final aRef = " +aRef.get());

输出:

aRef = a
Final aRef = null

从上面可以看出在只有弱引用的时候,gc会回收掉该内存

4. 虚引用(PhantomReference) -- 当GC认定一个PhantomReference是phantom reachable的时候,他会把他放入ReferenceQueue(构造hantomReference用到)中,虚引用不像软引用和弱引用,因为他们被enqueue到ReferenceQueue里了,所以不会自动被GC回收,一个对象是phantom reachable,那这个对象的phantom Reference只有在所有PhantomReference被清除或者unreachable的时候才会被清除

下面是phantom reachable解释:

  • An object is phantom reachable if it is neither strongly, softly,nor weakly reachable, it has been finalized, and some phantom reference refers to it.

(虚引用理解的还不是很清楚,上文大部分从官网翻译的一部分内容)

以下引用至:http://itlab.idcquan.com/Java/base/805949.html

Phantom 引用的用途较少,主要用于辅助finalize函数的使用。Phantom对象指一些对象,它们执行完了finalize函数,并为不可达对象,但是它们还没有被GC回收。这种对象可以辅助finalize进行一些后期的回收工作,我们通过覆盖Reference的clear()方法,增强资源回收机制的灵活性。

对象被清除的时候会调用该对象的finalize()方法(Java里没有析构函数概念)


从上面可以看出,索引的种类主要是影响 了内存回收的时机。这一点和C++的智能指针(针对动态内存分配后的释放管理)倒是有异曲同工之妙。


5. 包域名

在提包名之前,我们先看看,java编译运行文件里面所涉及的一些概念:

编译单元-- .java后缀的文件(每一个编译单元最多只有一个public类,且该类必须与文件名一致)

.java文件里的每一个class都被编译成为一个.class文件(class文件记录了每一个class相关的信息)

.jar文件(编译好的.class文件最终通过jar文档生成器压缩到一个jar文件里,jar文件记录了所有class文件压缩后相关的信息)

java解析器(通过java解析器我们通过jar文件去查找相应的class文件并装载和解释从而使用里面类文件的信息)

 

那么Java包域名在这个java编译装载解析的过程中起什么作用了?

Java包域名是用来避免命名冲突,访问权限和类文件查找控制,而C++里面只有文件,namespace和public,private,protected的概念

(一般情况下)包名会决定该文件存储路径(同时决定该类隶属于哪个包下-- 包访问权限问题)

Java里有包访问权限(同一个包里的class成员如果没有访问权限修饰符(public,protected,private),默认是friendly,即同一个包内的类可以互相访问其没有权限修饰符的成员和方法,但对于包外的类是private的。Java里同一个包里的类就相当于C++里同一个文件里的类可以相互访问)

不在同一个包内的类,java必须通过import关键词来导入该包(这里导入包的作用是导入所有隶属于该包内的class文件即所有该包内的类信息,这里和C++里的include概念差不多,都是将某一文件或包里的类信息加载进去)

在没有定义包名(即package)的文件中,默认是隶属于该目录的缺省包中,且为该目录包下的所有文件提供包访问权限

Java权限修饰词仅控制它所修饰的特定定义的访问权。C++里,访问权限修饰词可以控制气候的所有定义,除非另有访问权限词出现。

从上可以看出,Java里的访问权限主要是要多考虑一个包访问权限,还有就是包名会决定文件所在目录(一般情况)。当通过包名去访问类的时候和C++里的namespace在访问类的时候的功能是一致的

这里还有个要注意的点:

Java的继承还需要注意的不分public还是protected还是private继承,只根据成员的具体访问权限来定

 

Note:

Java包命名规则全部使用小写字母


6. 接口

Interface这个关键字产生了一个完全抽象的类,它根本就没有提供任何具体实现。它允许你通过创建一个能够被向上转型为不止一种基类型的类,来实现C++多重继承

Interface关键字比abstract的概念向前更买金了一步。你可以将它看做是“纯粹的”抽象类。它允许类的创建者为一个类建立其形式:有方法名,参数列表和返回类型,但是没有任何方法体。接口也可以包含数据成员,但是它们隐含都是static和final的。接口只提供了形式,而未提供实现。

implements关键字,他表示:“该接口是这个类的外貌,但是现在我要声明他是如何运作的”

interface的方法要声明为public,因为继承访问的权限的问题(接口的属性在缺省情况是static和final的,因为interface类是一种对事物的抽象,不会有任何的实现和对属性的变更,是所有实现该interface类共有的)

一个类可以继承任意多个接口(Java里的多重继承)。在Java中,你可以执行相同的行为,但是只有一个类可以有具体实现。

使用接口的原因:

1. 为了能够向上转型为不止一个的基类型

2. 防止客户端程序员创建该类的对象

普通的(非内部的)类,不能声明为private或protected;它们只可以被赋予public或者包访问权


7. 内部类

⑴内部类只在定义该类的作用域内有效(相当于局部类)(C++里的内部类相当于在外部类的命名空间下,但没有闭包的概念,所以要想访问外部类的信息必须是static的方法和成员或者保存外部类实例指针访问。C++闭包概念可以参考lambdaexpression)

⑵匿名内部类相当于new一个实现某一个接口类的子类(C++里的匿名类必须在定义的时候就创建对象,因为C++里的匿名类是一个没有名字的类的实例而不是继承了某个类的子类对象)

⑶匿名内部类要使用参数必须制定该参数是final的(C++里不用)

⑷匿名内部类通过实例初始化来模拟匿名类的构造函数(C++里因为是没有名字的类的实例,所以不存在构造函数)

(5)内部类对象隐含地保存了一个引用,指向创建它的外围类对象

内部类的继承必须初始化父类(enclosingClassReference.super())

(6)普通的内部类不能有static数据和static属性,也不能包含嵌套类。但是嵌套类可以包含这些东西

1. 要创建嵌套类的对象,并不需要其外围类的对象

         2. 不能从嵌套类的对象中访问非静态的外围类对象

学习实例如下:

<pre name="code" class="cpp">package hello;
public class HelloWorld {
    public static void main(String[] args){
        System.out.println("Hello World");
	class testInnerClass{
		testInnerClass()
		{
			System.out.println("testInnerClass() called");
		}
		String innerClassString()
		{
			return "innerClassString";
		}
	}	
	testInnerClass tic = new testInnerClass();
	System.out.println(tic.innerClassString());
	anonymousClass ac = anonymousC("testparameter");
	ac.anonymousClassFunction();
	System.out.println("nestClass.testSIMember = " + nestClass.testSIMember);
	//见⑸ -- 内部类对象隐含地保存了一个引用,指向创建它的外围类对象
	HelloWorld hw = new HelloWorld();
	noneStaticInnerClass nsnc = hw.getNSNC(); //因为内部类是非静态的,所以在必须通过定义该内部类的外部类的实例来返回。如果是static方法返回的话那么那个内部类必须是static的就是我们说的嵌套类。
	System.out.println("nestClass.getNestNSIMember() = " + 	nsnc.getNestNSIMember());
	nsnc.baseFunction();
	}

	//见⑶ -- 匿名内部类使用参数必须定义为final
	public static anonymousClass anonymousC(final String testparameter)
	{
		//见⑵ -- 匿名类
		return new anonymousClass(){
			public void anonymousClassFunction()
			{
				System.out.println("anonymousClassFunction() called");
				System.out.println("testparameter = " + testparameter);
			}
		};
	}
	public static class nestClass{
		nestClass()
		{
			//这里testInnerClass是内部类,所以我们无法在main()方法以外的地方定义testInnerClass实例对象 -- 见⑴
			//testInnerClass tic = new testInnerClass();
			System.out.println("nestClass()");
		}
		//见⑹ --普通的内部类不能有static数据和static属性,也不能包含嵌套类。但是嵌套类可以包含这些东西(普通类需要外部类信息来初始化实例,而嵌套类是static的,是属于该类而不是属于某个普通类实例的,不符合普通内部类设计理念,所以普通类不能包含嵌套类)
		static int testSIMember = 2;
	}		
	public class NSNCBase{
		NSNCBase()
		{
			System.out.println("NSNCBase() called");
		}
		void baseFunction()
		{
			System.out.println("baseFunction() called");
		}
	}
	
	public class noneStaticInnerClass extends NSNCBase{
		noneStaticInnerClass ()
		{
			//见⑸ -- 内部类的继承必须初始化父类
			super();
			System.out.println("noneStaticInnerClass ()");
			//内部类访问外部类:外部类类名.this
			HelloWorld.this.helloWorldFunction();
		}
		int getNestNSIMember()
		{
			return testNSIMember;
		}
		int testNSIMember = 1;
		//见⑹ --普通的内部类不能有static数据和static属性,也不能包含嵌套类。但是嵌套类可以包含这些东西
		//static int testSIMember = 2;
	}		
	interface anonymousClass{
		public void anonymousClassFunction();
	}
	private noneStaticInnerClass getNSNC()
	{
		return new noneStaticInnerClass ();
	}
	public void helloWorldFunction()
	{
		System.out.println("helloWorldFunction() called");
	}
}


 

输出:

Hello World
testInnerClass() called
innerClassString
anoymousClassFunction() called
anonymousMember = testparameter
nestClass.testSIMember = 2
NSNCBase() called
noneStaticInnerClass () called
helloWorldFunction() called
nestClass.getNestNSIMember() = 1
baseFunction() called


注意普通内部类和嵌套类的区别(嵌套类是static的)

 

闭包与回调:

闭包 -- 是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域

Java中没有指针的概念,回调的实现通过内部类提供闭包的功能是完美的解决方案(当继承与接口实现冲突时,我们可以通过内部类去实现接口,然后保存接口类的reference去决定什么时候调用回调,让外部类与回调接口分离开来)

package hello;
public class HelloWorld {
    public static void main(String[] args){
        	System.out.println("Hello World");
			
	//inner class closure callback study
	Callee c = new Callee();
		
	Caller caller = new Caller(c.getCallbackReference());
		
	caller.go();		
		
	caller.go();
		
	//test interface member access authority
	childInterfaceClass cic = new childInterfaceClass();	
	cic.testDefaulltPublicFunction();
	System.out.println("cic.interfaceMember = " + cic.interfaceMember);
	}
}

//inner class closure callback study
interface Incrementable{
	void increment();
}

class MyIncrement{
	void increment()
	{
		System.out.println("MyIncrement.increment()");
	}
	static void f(MyIncrement mi)
	{
		mi.increment();
	}
}

class Callee extends MyIncrement{
	private int i = 0;
	private void incr(){
		i++;
		System.out.println(i);
	}
	//内部类实现接口解决了父类继承方法和接口方法冲突的问题,通过保存实现该接口方法的内部类索引来调用回调与外部类继承方法区分开。
	private class Closure implements Incrementable{
		public void increment()
		{
			//闭包 -- 是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域(这里创建该内部类的外部类既是该类的作用于,可访问该外部类非私有成员数据和方法)
			incr();
		}
	}
	
	Incrementable getCallbackReference(){
		return new Closure();
	}
}

class Caller{
	private Incrementable callbackReference;
	Caller(Incrementable cbh)
	{
		callbackReference = cbh;
	}
	void go()
	{
		callbackReference.increment();
	}
}

//test interface access authority
interface testInterfaceMember{
	void testDefaulltPublicFunction();
	//Interface class's default access authority is static
	//int interfaceMember = 1;
}

class childInterfaceClass implements testInterfaceMember{
	public void testDefaulltPublicFunction()
	{
		System.out.println("testDefaulltPublicFunction() called");
		//interfaceMember++;
	}
}

输出:

Hello World
i = 1
i = 2
testDefaultPublicFunction() called
cic.interfaceMember = 1

为什么需要内部类?

每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。

内部类使得多重继承的解决方案变得完整,接口解决了部分问题,而内部类有效地实现了“多重继承”


8. 类型检查

运行期类型识别(RTTI,run-time type identification)

两种:

1. 传统的RTTI,假定我们在编译器和运行期已经知道了所有的类型 (Java里面是通过Class对象(它包含了与类有关的信息),每个类都有一个Class对象,在运行期,一旦我们想生成一个类的一个对象,运行这个程序的Java虚拟机(JVM)首先检查这个类的Class对象是否已经加载。如果尚未加载,JVM就会根据类名查找.class文件,并将其载入)

通过Class对象加载类 --Class.forName("ClassNameFullPath"); (Class提供了很多方法去获取类的相关信息)

 类字面常量(Class literal)  -- ClassName.class;

判断变量类型(instanceof)-- variable instanceof ClassName

动态instanceof-- Class.isInstance(variable)

instanceof 和 Class的区别,instanceof保持了类型的概念,即使是派生类也判断为true。Class是判断是否是某个确切类,不包含派生类

2. 反射机制(reflection),它允许我们在运行期获得类的信息,用来检测可用的方法并返回方法名

java.lang.reflect

RTTI和反射之间真正的区别只在于,对RTTI来说,编译器在编译器打开和检查.class文件。而对于反射机制来说.class文件在编译器是不可获取的,所以是在运行期打开和检查.class文件 (更多学习还得参见官网)

在使用反射之前,需要了解一下,Java里面如何加载class文件的

ClassLoader是负责类文件加载的类

ClassLoader主要的有三个:

1. Booststrap class loader

is responsible for loading standard JDK class files formrt.jar and it is parent of all class loaders in Java. (主要负责加载标准库里面的类)

2. Extension class loader

delegatesclass loading request to its parent, Bootstrap and if unsuccessful, loads classfrom jre/lib/ext directory or any other directory pointed by java.ext.dirssystem property. (负责加载java.ext.dirssystem指向的目录,比如jre/lib/ext)

3. System class loader

is adescendant of the bootstrap class loader. It is responsible for loading in the application, as well as for loading classes and resources in the application's classpath. (负责程序classpath指定类)

三个class loader之间的关系如下:


三个class loader工作结构如下:


ClassLoader遵循三个基本准则:

1. delegation

forward request of class loading to parent class loader and only loads the class, if parent is not able to find or load class (只能加载父类classloader没有加载的类 -- 顺序性)

2. visibility

allows child class loader to see all the classes loaded by parent ClassLoader, but parent class loader cannot see classes loaded by child (子类classloader可以看到所有父类加载的类,但反过来却不行 -- 可视性)


3. uniqueness

allows to load a class exactly once, which is basicallyachieved by delegation and ensures that child ClassLoader doesn't reload theclass already loaded by parent (每个类只能被加载一次 -- 单一性)

如果想要自己去加载新的类,可以通过继承ClassLoader并重写loadClass方法实现。

ClassLoader和reflection学习例子如下:

Java_Study.java

package java_study;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.Class;

import java.lang.reflect.ReflectPermission;
import java.lang.reflect.InvocationTargetException;

import java.lang.reflect.Field;
import java.lang.annotation.Annotation;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

import java.util.*;

import packageclass.*;

class Log{
	public static void print(String temp)
	{
		System.out.println(temp);
	}
}

class MyClassLoader extends ClassLoader{
	public MyClassLoader(ClassLoader parent){
		super(parent);
	}
	
	public Class loadClass(String name) throws ClassNotFoundException{
		if( !"hello.BaseClass".equals(name))
		{
			return super.loadClass(name);
		}
		try {
			String url = "file:D:/Java_Study/Ant_Study_With_Java/build/classes/hello/BaseClass.class";
			URL myUrl = new URL(url);
			URLConnection connection = myUrl.openConnection();
			InputStream input = connection.getInputStream();
			ByteArrayOutputStream buffer = new ByteArrayOutputStream();
			int data = input.read();
			
			while( data != -1 )
			{
				buffer.write(data);
				data = input.read();
			}
			
			input.close();
			
			byte[] classData = buffer.toByteArray();
			
			return defineClass("hello.BaseClass", classData, 0, classData.length);
			
		} catch (MalformedURLException e) {
			// TODO: handle exception
			e.printStackTrace();
		} catch( IOException e){
			e.printStackTrace();
		}
		return null;
	}
}

public class Java_Study extends Base{

	public Java_Study()
	{
		Log.print("Java_Study Constructor Called");
		Log.print("Base::protect_member = " + protect_member);
	}


	protected void accessTest() {
		Log.print("accessTest Called");
		baseCall();
	}
	
	protected void baseCall() {
		Log.print("Java_Study::baseCall() Called");
		super.baseCall();
	}
	
	void defaultPackageAccessTest(){
		Log.print("defaultPackageAccessTest() Called");
	}
	
	protected void protectedPackageAccessTest() {
		Log.print("protectedPackageAccessTest() Called");
	}
	
	public static void main(String[] args)
	{
		Log.print("Hello World!");
		System.out.println(new Date());
		
		Log.print("ClassLoader Study");
	
		try {
			ClassLoader parentClassLoader = Java_Study.class.getClassLoader();
			MyClassLoader classLoader = new MyClassLoader(parentClassLoader);
			Class myObjectClass = classLoader.loadClass("hello.BaseClass");
			Object myObjectClassIns = myObjectClass.newInstance();
			Method bo1 = myObjectClass.getDeclaredMethod("secondInterfaceFunction");
			
			bo1.invoke(myObjectClassIns);
		} catch (ClassNotFoundException x) {
			Log.print(x.toString());
		} catch (NoSuchMethodException x) {
			Log.print(x.toString());
		} catch (IllegalAccessException x) {
			Log.print(x.toString());
		} catch (InvocationTargetException x) {
			Log.print(x.toString());
		} catch( InstantiationException e){
			Log.print(e.toString());
		}
		

		Log.print("Reflection Study");
		
		A a1 = new A();
		Class a = a1.getClass();
		
		Class a2 = A.class;
		Class a3 = Double.TYPE;
		
		try {
			Class a4 = Class.forName("java_study.A");
			Object a4i = a4.newInstance();
			
			Log.print("test print");
			
			Field[] af = a4.getDeclaredFields();
			
			if( af.length != 0 )
			{
				for(Field ia: af)
				{
					Log.print(ia.toGenericString());
				}
			}
			else
			{
				Log.print("af.length == 0");
			}
			
			String arg1 = "method1 arg";
			Method am1 = a4.getDeclaredMethod("method1",String.class);
			
			am1.invoke(a4i,(Object)arg1);
			
			
			Method am2 = a4.getDeclaredMethod("method2");
			
			am2.invoke(a4i);
			
			Class[] argTypes = new Class[]{ String[].class };
			String[] arg3 = {"arg3-1","arg3-2"};
			Method am3 = a4.getDeclaredMethod("method3",argTypes);
			
			am3.invoke(a4i,(Object)arg3);
			
			String arg4 = "arg4-1";
			Method am4 = a4.getDeclaredMethod("smethod4",String.class);
			//If we call static method in class, we only need to pass null instead of class instance
			am4.invoke(null,(Object)arg4);
			
			//Example for severial different types parameter
			Class[] argTypesTwo = {String.class, int.class};
			Object[] arg5 = {"arg5", 5};
			Method am5 = a4.getDeclaredMethod("method5",argTypesTwo);
			am5.invoke(a4i,arg5);
			
			Method am6 = a4.getDeclaredMethod("method6");
			//Set accessible to true, then we can access private method outside
			am6.setAccessible(true);
			
			am6.invoke(a4i);
		} 
		catch (ClassNotFoundException x) {
			Log.print(x.toString());
		} catch (NoSuchMethodException x) {
			Log.print(x.toString());
		} catch (IllegalAccessException x) {
			Log.print(x.toString());
		} catch (InvocationTargetException x) {
			Log.print(x.toString());
		}catch( InstantiationException x){
			Log.print(x.toString());
		}
		
		
		
		try {
			Class p = Class.forName("packageclass.PackageClass");
			Object pi = p.newInstance();
			
			Log.print("test print2");
			
			Field[] pf = p.getDeclaredFields();
			
			if( pf.length != 0 )
			{
				for(Field ip: pf)
				{
					Log.print(ip.toGenericString());
				}
			}
			else
			{
				Log.print("af.length == 0");
			}
		
			} 
		catch (ClassNotFoundException x) {
			Log.print(x.toString());
		} catch (SecurityException x) {
			Log.print(x.toString());
		} catch (IllegalAccessException x) {
			Log.print(x.toString());
		}catch( InstantiationException x){
			Log.print(x.toString());
		}
	}
}

A.java

package java_study;

public class A /*extends Object*/{
	//Refelection Class A
	public A()
	{
		Log.print("java_study.A() Called");
	}
	
	public void method1(String arg) 
	{
		Log.print("A::method1() called" + arg);
	}
	
	public void method2()
	{
		Log.print("A::method2() called");
	}
	
	public void method3(String[] arg)
	{
		String allarg = "";
		for( int i = 0; i < arg.length; i++ )
		{
			allarg += arg[i];
		}
		Log.print("A::method3() called " + allarg);
	}
	
	public static void smethod4(String arg)
	{
		Log.print("static A::method4() called" + arg);
	}
	
	public void method5(String arg1, int arg2)
	{
		Log.print("A::method5() called " + arg1 + " " + arg2);
	}
	
	private void method6()
	{
		Log.print("A::method6() called");
	}
	
	
	public int value;
}

BaseClass.java

package hello;

public class BaseClass{
	public void secondInterfaceFunction()
	{
		System.out.println("secondInterfaceFunction() called in BaseClass");
	}
}

输出结果:

稍后上传


通过上面我们可以看出,反射结合classloader可以动态的获得class文件并加载后并实例化该类对象来调用里面的方法和访问成员(private的方法也可以通过setAccessible(true)来实现访问)。主要用于编译时不知道类文件信息的情况。

反射的缺点:

引用至 -- http://tutorials.jenkov.com/java-reflection/dynamic-class-loading-reloading.html
http://sanjaal.com/java/tag/drawbacks-of-java-reflections/

Drawbacks of Reflection
Reflection is powerful, but should not be used indiscriminately. If it is possible to perform an operation without using reflection, then it is preferable to avoid using it. The following concerns should be kept in mind when accessing code via reflection.

  • Performance Overhead
    Because reflection involves types that are dynamically resolved, certain Java virtual machine optimizations can not be performed. Consequently, reflective operations have slower performance than their non-reflective counterparts, and should be avoided in sections of code which are called frequently in performance-sensitive applications.
  • Security Restrictions
    Reflection requires a runtime permission which may not be present when running under a security manager. This is in an important consideration for code which has to run in a restricted security context, such as in an Applet.
  • Exposure of Internals
    Since reflection allows code to perform operations that would be illegal in non-reflective code, such as accessing private fields and methods, the use of reflection can result in unexpected side-effects, which may render code dysfunctional and may destroy portability. Reflective code breaks abstractions and therefore may change behavior with upgrades of the platform.

主要是性能(类型动态解决)和安全性上(比如访问私有成员)的问题

ClassLoader学习参考资料:

http://javarevisited.blogspot.com/2012/12/how-classloader-works-in-java.html

http://javarevisited.blogspot.ca/2011/01/how-classpath-work-in-java.html

http://tutorials.jenkov.com/java-reflection/dynamic-class-loading-reloading.html

......


9. 并发

动机:

使用并发最强制性的原因之一就是要产生能够作出相应的用户界面

基本线程:

java.lang.Thread

Thread最重要的方法是run(),run()里的代码能够与程序里的其他线程"同时"执行

每个线程由一个唯一的数字来标识,这个数字由一个静态成员变量产生。

线程的优先权会决定线程被执行的频率

 

后台(daemon)线程:

所谓“后台”线程,是指程序运行的时候,在后台提供一种通用服务的线程

当所有的非后台线程结束,程序也就终止了

Note:

因为Java里面匿名内部类的写法,我们可以写成如下:

public void newThread()
{
	Thread thread = new Thread(new Runnable() {
			@Override
			public void run() {
				//Do something here
			}
		});
}

10. 正则表达式

定位:正则表达式是强大,便捷,高效的文本处理工具

术语:

正则(regex) -- 正则表达式,决定匹配规则

匹配(matching) -- 正则表达式在字符串里匹配的字符串

元字符(metacharacter)-- 正则表达式里特殊作用的字符

字表达式(subexpression) -- 正则表达式中的一部分,通常是括号内的表达式

字符(character) -- ......

 

元字符:

^ --单独使用表示一行的开始

$ --代表一行的结束

- -- 表示一个范围

^ -- 用于[]内的时候表示除开这些

. -- 匹配任意字符

| -- or的意思

? -- 代表可选项零次或一次

+ -- 代表出现一次或多次

* -- 代表出现任意次或不出现

......

Note:

元字符在字符组外面的时候作用才是元字符

转义字符:

\ -- 用于匹配元字符用作字符查找的时候

 

Note:

正则表达式对大小写区分

不同的语言里对正则表达式有自己的一些特殊支持,比如一些参数修饰符......。元字符的支持程度也不一样。

 

环视-- 环视结构不匹配任何字符,只匹配文本中的特定位置

环视在检查字表达式能否匹配的过程中,它们本身不会“占用”任何文本,只是选择一个位置

 

Java里面正则表达式包含更多反斜杠的原因:

Java要求正则表达式必须以字符串方式提供。正则表达式中的反斜线必须转义,以避免Java在解析字符串时按照自己的方式处理它们

 

字符编码:

不同的字符编码决定了不同的字符映射规则

正则表达式对编码的支持会影响正则表达式的匹配(因为不同的编码,映射表不一样,表达单个字符所使用的字节总数也不一定一样)

有些字符编码对组合字符(用多个字符组合表示一个字符)的支持会影响正则表达式的匹配

行终止符会印象文件的读入方式,影响正则表达式里行相关的元字符使用

区块表示Unicode字符映射表中一定范围内的代码点。

......

表达式的匹配原理:

正则引擎的分类:

1. DFA (文本主导)-- 确定型有穷自动机

与表达式主导的NFA不同,DFA引擎在扫描字符串时,会记录“当前有效”的所有匹配可能

(DFA引擎是确定型的-- 目标文本中的每一个字符只会检查(最多)一遍。对于一个已经匹配的字符,你无法知道它是否属于最终匹配(它可能属于最终会失败的匹配),但因为引擎同时记录了所有可能的匹配,这个字符只需要检查一次。)

 

2. NFA (表达式主导)-- 非确定型有穷自动机

优点:

每个字表达式都是独立的

Perl,PHP,Java,.NET都是使用NFA引擎

NFA引擎重要的概念:

回溯 (因为是表达式主导,在一次处理各个子表达式的时候会遇到选择,当分支出错后需要回溯去尝试其他分支)

回溯原则:

如果需要在“进行尝试”和“跳过尝试”之间选择,对于匹配优先量词,引擎会优先选择“进行尝试”,而对于忽略优先量词,会选择“跳过尝试”

(注意匹配优先和忽略优先会导致查找过程中走的路的顺序改变)

(固化分组会放弃某些可能的路径,根据具体情况的不同,放弃备用状态(这里的备用状态指回溯的一些路径)可能导致不同结果)

环视就是利用了放弃备用状态而实现顺序或逆序查询第一个满足条件的功能

 

一般情况下,文本主导的DFA引擎要快一些。正则表达式主导的NFA引擎,因为需要对同样的文本尝试不同的字表达式匹配,可能会浪费时间,

 

(通过上面的学习,在我看来DFA和NFA由于文本和表达式主导的区别,导致了搜索顺序的路径的区别,也决定了搜索的复杂度)

更多学习参见: 《精通正则表达式》






http://tutorials.jenkov.com/java-reflection/dynamic-class-loading-reloading.html
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值