多态,父类的引用指向子类的对象,使父类的引用呈现出子类的特性。好处在于减少了类之间的耦合性,例如:
public Abstract Class Car
{
void run(){...};
}
public class BMW extends Car
{
//子类实现自己的run方法
void run(){...}
}
public Class MyCar
{
runCar(Car car)
{
car.run();
}
}
通过父类的引用来调用子类的方法,如此可以免去为每种车定义不同的runCar方法,如runBMW(BMW bmw),从而是Car和MyCar两个类解耦。
1)从语法上,一个是abstract class,一个是interface,一个类可以实现多个接口,却只能继承一个抽象类,抽象类中可以有成员变量,方法可以有实现,但是接口中的变量只能是static finall的。
2)从含以上,抽象类与子类的关系是“is a”,而接口是“like a”,它赋予一个类更多的行为,是类属性与行为上更高层次上的抽象。如:给汽车赋予报警器的功能,这时候Car可以implements Alarm接口,并实现Alarm接口中的方法。
反射是允许程序在运行时对自身进行检查(自审,Introspection),动态获取类的信息(类名,方法,成员变量,构造函数)和调用对象方法的特性。例如,
Class> clazz = Class.forName("java.lang.Object");
Object obj = clazz.newInstance();
Methdo method = clazz.getMethod("方法名",new Class[](int.Class,int.Class));
Object result = method.invoke();
应用举例——java动态代理
1)定义真实类的接口,及其实现
public interface MyAction {
public void doAction();
}
public class Action1 implements MyAction{
public void doAction()
{
System.out.println("from Action1!!");
}
}
2)定义代理类中,代理过程的实现
public class MyInvocationHandler implements InvocationHandler{
private Object obj;
public MyInvocationHandler(Object obj)
{
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("before"+method);
method.invoke(obj, args);
System.out.println("after"+method);
return null;
}
}
3)通过java的Proxy类动态生成代理类,返回一个真实类的接口,
public class ProxyTest {
public static void main(String[] args)
{
Action1 action1 = new Action1();
MyInvocationHandler myInvocationHandler = new MyInvocationHandler(action1);
MyAction action = (MyAction)Proxy.newProxyInstance(action1.getClass().getClassLoader(), action1.getClass().getInterfaces(),myInvocationHandler);
action.doAction();
System.out.println(action.getClass());
}
}
寄存器、栈(进程独享)、堆(共享)、静态区(方法去)、常量池(每个类都维护一个自己的常量池)。
堆区:
1.存储的全部是对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令)
2.jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身
栈区:
1.每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象),对象都存放在堆区中
2.每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。
3.栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。
方法区:
1.又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。
2.方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。
一个例子:
public class AppMain //运行时, jvm 把appmain的信息都放入方法区
{
public static void main(String[] args) //main 方法本身放入方法区。
{
Sample test1 = new Sample( " 测试1 " ); //test1是引用,所以放到栈区里, Sample是自定义对象应该放到堆里面
Sample test2 = new Sample( " 测试2 " );
test1.printName();
test2.printName();
}
} Sample.java
public class Sample //运行时, jvm 把appmain的信息都放入方法区
{
/** 范例名称 */
private name; //new Sample实例后, name 引用放入栈区里, name 对象放入堆里
/** 构造方法 */
public Sample(String name)
{
this .name = name;
}
/** 输出 */
public void printName() //print方法本身放入 方法区里。
{
System.out.println(name);
}
}
首先,系统收到了我们发出的指令,启动了一个Java虚拟机进程,这个进程首先从classpath中找到AppMain.class文件,读取这个文件中的二进制数据,然后把Appmain类的类信息存放到运行时数据区的方法区中。这一过程称为AppMain类的加载过程。
接着,Java虚拟机定位到方法区中AppMain类的Main()方法的字节码,开始执行它的指令。这个main()方法的第一条语句就是:
Sample test1=new Sample("测试1");
语句很简单啦,就是让java虚拟机创建一个Sample实例,并且呢,使引用变量test1引用这个实例。貌似小case一桩哦,就让我们来跟踪一下Java虚拟机,看看它究竟是怎么来执行这个任务的:
1、 Java虚拟机一看,不就是建立一个Sample实例吗,简单,于是就直奔方法区而去,先找到Sample类的类型信息再说。结果呢,嘿嘿,没找到@@,这会儿的方法区里还没有Sample类呢。可Java虚拟机也不是一根筋的笨蛋,于是,它发扬“自己动手,丰衣足食”的作风,立马加载了Sample类,把Sample类的类型信息存放在方法区里。
2、 好啦,资料找到了,下面就开始干活啦。Java虚拟机做的第一件事情就是在堆区中为一个新的Sample实例分配内存, 这个Sample实例持有着指向方法区的Sample类的类型信息的引用。这里所说的引用,实际上指的是Sample类的类型信息在方法区中的内存地址,其实,就是有点类似于C语言里的指针啦~~,而这个地址呢,就存放了在Sample实例的数据区里。
3、 在JAVA虚拟机进程中,每个线程都会拥有一个方法调用栈,用来跟踪线程运行中一系列的方法调用过程,栈中的每一个元素就被称为栈帧,每当线程调用一个方法的时候就会向方法栈压入一个新帧。这里的帧用来存储方法的参数、局部变量和运算过程中的临时数据。OK,原理讲完了,就让我们来继续我们的跟踪行动!位于“=”前的Test1是一个在main()方法中定义的变量,可见,它是一个局部变量,因此,它被会添加到了执行main()方法的主线程的JAVA方法调用栈中。而“=”将把这个test1变量指向堆区中的Sample实例,也就是说,它持有指向Sample实例的引用。
OK,到这里为止呢,JAVA虚拟机就完成了这个简单语句的执行任务。参考我们的行动向导图,我们终于初步摸清了JAVA虚拟机的一点点底细了,COOL!
接下来,JAVA虚拟机将继续执行后续指令,在堆区里继续创建另一个Sample实例,然后依次执行它们的printName()方法。当JAVA虚拟机执行test1.printName()方法时,JAVA虚拟机根据局部变量test1持有的引用,定位到堆区中的Sample实例,再根据Sample实例持有的引用,定位到方法去中Sample类的类型信息,从而获得printName()方法的字节码,接着执行printName()方法包含的指令。
将二进制Class文件加载到java内存中的方法区中。步骤
1)加载;
2)链接:验证;准备(为类的静态变量分配内存,并初始化为默认值);解析(将符号引用转化为直接引用)
3)初始化,为类的静态变量赋予正确的初始值。
注:只有当类或接口被首次主动使用时,才进行初始化步骤。
主动使用包括:
1)创建类实例;
2)方位某个类或接口的静态变量;
3)反射,Class.forName;
4)初始化一个类的子类;
5)被标明是启动类的类,包含main的类;
加载规则:父类委托
加载器类型:
1)根加载器(bootstrap),加载javahome/lib
2)扩展加载器,加载javahome/lib/ext
3)系统加载器,加载自定义类,是用户自定义类加载器的父类。
自定义ClassLoad——实现从自定义路径加载类
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
public class MyClassLoader extends ClassLoader
{
private String name;
private String path = "d:\\";
private final String fileType = ".class";
public MyClassLoader(String name) {
super();
this.name = name;
}
public MyClassLoader(ClassLoader parent, String name)
{
super(parent);
this.name = name;
}
@Override
public String toString()
{
return this.name;
}
protected Class> findClass(String name) throws ClassNotFoundException
{
byte[] data = loadClassData(name);
return this.defineClass(name,data, 0, data.length);
//throw new ClassNotFoundException(name);
//return clazz;
}
private byte[] loadClassData(String name)
{
byte[] data = null;
InputStream is = null;
ByteArrayOutputStream bos = null;
try
{
name = name.replace(".", "\\");
is = new FileInputStream(new File(path+name+fileType));
bos = new ByteArrayOutputStream();
int ch = 0;
if(-1 != (ch= is.read()))
{
bos.write(ch);
}
data = bos.toByteArray();
}
catch (Exception e) {
e.printStackTrace();
}
finally
{
try
{
bos.close();
is.close();
}
catch(Exception e)
{
e.printStackTrace();
}
}
return data;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getFileType() {
return fileType;
}
public static void main(String[] args)throws Exception
{
MyClassLoader loader1 = new MyClassLoader("loader1");
loader1.setPath("G:\\test\\server\\");
MyClassLoader loader2 = new MyClassLoader(loader1,"loader2");
loader2.setPath("G:\\test\\client\\");
MyClassLoader loader3 = new MyClassLoader(null, "loader3");
loader3.setPath("G:\\test\\other\\");
//test(loader1);
//test(loader2);
//test(loader3);
Class clazz = loader1.loadClass("Sample");
Object obj = clazz.newInstance();
System.out.println(clazz.hashCode());
loader1 = null;
clazz = null;
obj = null;
loader1 = new MyClassLoader("loader1");
loader1.setPath("G:\\test\\server\\");
clazz = loader1.loadClass("Sample");
obj = clazz.newInstance();
System.out.println(clazz.hashCode());
}
public static void test(ClassLoader classLoader)throws Exception
{
Class clazz = classLoader.loadClass("Sample");
Object object = clazz.newInstance();
}
}
步骤:
1)加载驱动类
Class.forName("com.mysql.jdbc.Driver");
2)获取连接
Connection conn = DriverManager.getConnection("连接字","用户名","密码");
例子:
public class Test {
public static void main(String[] args) {
try {
// 加载MySql的驱动类
Class> clazz = Class.forName("com.mysql.jdbc.Driver");
System.out.println(clazz.getClassLoader());
} catch (ClassNotFoundException e) {
System.out.println("找不到驱动程序类 ,加载驱动失败!");
e.printStackTrace();
}
String url = "jdbc:mysql://localhost:3306/test";
String username = "root";
String password = "123456";
try {
String sql = "select * from custom";
String sql1 = "select * from custom where id = ?";
Connection conn = DriverManager.getConnection(url, username,
password);
PreparedStatement pstmt = conn.prepareStatement(sql1) ;
pstmt.setInt(1, 1);
/*Statement statement = conn.createStatement();
ResultSet rs = statement.executeQuery(sql);*/
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
String name = rs.getString("name");
String pass = rs.getString(3); // 此方法比较高效
System.out.println(name + "**" + pass);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
使用PreparedStatement的好处:
1)有效的利用了数据库的缓存几只,DB会缓存部分重复使用的语句,如果是Statement的话,每次查询条件稍有不同就认为是不同语句,无法使用缓存
2)防止sql注入,如sql语句中有"select * from custom where id" = + username ,username是网页传来的值,那么username="yang or 1==1"就能使查询条件用为为真,从而查询到所有的信息。
DAO Factory
组件:
1)数值对象(VO)
public class CustomBean {
}
2)DAO接口
public interface CustomDao {
public List findAll();
}
3)DAO接口实现类
public class CustomDaoImp implements CustomDao {
@Override
public List findAll() {
// TODO Auto-generated method stub
return null;
}
}
public class CustomDaoImpHibernate implements CustomDao{...}
4)DAO工厂
public class DaoFactory {
public static CustomDao getCustomDao()
{
return new CustomDaoImp();
//return new CustomDaoImpHibernate();
}
}
优点:
1)实现了数据访问与业务逻辑的分离;
2)引入了Factory模式,通过工厂返回接口,隐藏了具体数据库操作的实现,与具体代码实现分离,可以是Hibernate或者jdbc,因此代码的修改只需修改DaoFactory中的返回的对象。
String不可变字符串,后两种通过append可以使字符串增长(要预留空间,所以初始化时要指定合适的长度,避免内存浪费)。
StringBuffer 与 StringBuilder 中的方法和功能完全是等价的,只是StringBuffer 中的方法大都采用了 synchronized 关键字进行修饰,因此是线程安全的,而 StringBuilder 没有这个修饰,可以被认为是线程不安全的。
StringBuffer 始于 JDK 1.0
StringBuilder 始于 JDK 1.5
从 JDK 1.5 开始,带有字符串变量的连接操作(+)如String s +="aa";,JVM 内部采用的是StringBuilder 来实现的,而之前这个操作是采用 StringBuffer 实现的。