我的java学习笔记(血泪史之一)-201…

突然之间想到把之前的笔记都翻出来,毕竟是自己刚入门的时候真正付出过时间和经历的东西,比较有意义.
不多说..之间发出来(话说之前都是用的txt写比较,too young too navie,500多行)
我们导入到jar包,当要使用其中的类时,jvm是直接装载该类的字节码文件,还是要先编译下.java的源文件,然后生成一个新的字节码文件?
给我们的jar包里面是只有字节码文件还是只有java文件,还是二者都有;
貌似只有.class文件,因为我们要查看源代码,还要有src包;


要养成写注释的好习惯,但是注释要写的精炼恰当;既要让人看的懂,又不显得那么繁琐;乱七八糟;即该要的必须有,不该要的不要有;
注释三部曲:
1 需求:
2 思路:
3 步骤:
Java中函数的重载只和参数列表有关,跟返回值没有任何关系,也即只要函数名相同,参数列表(参数类型和参数个数)不同就算重载,当然前提是在同一个类中;


栈内存中的定义的变量会自动销毁,堆内存中的数据会由垃圾回收机制回收;
内存泄露的示例:
1:Set set=new HashSet();
Student student=new Student();
student.setName("小强");
set.add(student);
student.setName("小宝");
set.delete(student);//删除不成功,因为,hash值改变了;
也就是说一个对象的hash值跟他的属性值有关,属性值改变了,hash值就变了;
内存泄露还有一种情况;
就是我们使用匿名的流,使用了一次,但是没法关;
即 new FileOutputStream("filename");



Java中有五种内存形式:栈内存(int arr定义出来的),堆内存(new出来的);方法区(数据区,共享区),本地方法区,寄存器,

定义Boolean型的数组,其默认值为false,
Java中有数据类型在大地方面分为两种:基本数据类型比如,boolean和int,short,long,float,double,char byte等8种;
和引用数据类型,如数组,枚举,类类型等,而且只有引用数据类型才能给其赋值为null,基本数据类型是不能被赋值为null的;

关于基本数据类型和String类型的常量,在编译时会发生代换;


数组越界异常是运行时异常,运行时异常RuntimeException是Exception的子类;

堆内存中的对象都有一个初始化值,即null

在定义类中的成员时,首先考虑到该成员是否需要被共享,即该数据并不需要给每个对象都定义一个,
而是所有的对象共享该成员,那么就要把它定义成静态,在内存中对应的是方法区中;而非静态成员定义在堆内存中;
一个类会不会被加载要看它是否被使用,即要使用到该类中的内容,而仅仅是创建该类对象的引用不能说是使用到该类,
因为仅仅是创建该类对象的引用,该引用存在于栈内存中,并没有指向堆内存和方法区;
当然我们也可手动加载该类,同过Class.forName();方法;


构造代码块中可以引用非静态变量;
对于对象及成员的初始化问题;静态代码块,默认初始化,构造代码块,构造函数,这是一个类中都有的东西,当我们在另一个类中new它 的时候,具体的执行步骤为:
1 首先该类的字节码被加载进内存,同时静态代码块执行。
2 当新建该对象时,在堆内存中开辟内存空间,同时执行构造代码块中的内容
3 为成员变量进行默认初始化;
4 为成员变量进行显示初始化
5 调用构造函数进行初始化,如果构造函数内部还有更细节化的构造函数初始化,则
调用更细节的构造函数进行初始化;(父类)

虽然静态方法也可以被覆盖,但是这种操作一般不建议使用,
子类覆盖父类的方法有一定的前提,
1即子类要覆盖父类中的方法,该方法不能是私有权限,
即父类应该对外提供该方法,否则子类不知道有该方法,就不存在覆盖;
2 子类方法的权限要大于等于父类方法的权限;
保护权限高于默认权限;
子类覆盖父类的方法的最低权限只能是protected权限,这时父类可以是protected权限;

抽象类中可以不定义抽象方法,abstract关键字可以修饰类和方法,不能修饰变量;如果我们希望某个类不创建对象,就可以加上抽象化;
同时我们可以在该类中定义抽象类的引用,并new一个其匿名的实现类;算是匿名类的高级应用吧。。。



模板设计模式:
基本思想,我们要定义处理某一类问题的模板方法,当下次使用时,可以直接使用,而不用再去定义;
核心思想是我们要定义这类问题都要用到底代码,并暴露出去,如果不希望子类复写,可以使用final修饰;
而需要子类自己定义的内容,我们可以定义好或者干脆把他定义成抽象的;由子类去复写;
而且可以在子类中定义公共的内容,并使用到抽象的内容,即需要子类复写的内容;

子类继承父类,如果父类中有静态成员,子类同样可以继承过来使用该静态成员;该成员包括成员变量和成员函数;
接口之间可以多继承
多态情况下,成员变量不管是运行还是编译都看左边,同时静态成员都是看左边;

实现多线程技术最好使用实现Runnable接口,为什么呢?
因为Java只支持多实现,不支持多继承。如果一旦继承了Thread类,
那么他就不能在继承其他类的;也就是出现了局限性;
同时使用继承抽象类和实现接口的区别:
1 使用抽象类可以使用部分父类已经写好的方法,那么子类可以使用,如果不想使用也可以覆盖掉,而且并不是所有的方法都是抽象的(抽象类中的方法可以全部都是非抽象的),
那么子类使用起来就相当顺手;但是使用继承的话,就只能继承这一个,而不能再去继承其他类;对于非抽象方法子类可以选择是否覆盖,决定权在程序员;
2 实现接口的话,可以多实现。将要被线程执行的代码放到一个对象中,更加的面向对象;
即将启动线程和要被线程执行的代码分开存放;



同时注意的是:Java面向的第一原则是面向接口编程;所以提倡用接口去实现;
匿名类也可以是非抽象类的子类;

线程的五种状态:
创建;
冻结
阻塞
启动
消亡:
线程结束:被多线程执行的代码结束线程就消亡了,比如run方法,如果run方法中的代码执行结束线程就结束了;
毕老师说的改变标记,其实就是要让run方法执行结束;因此可以说,线程的生命周期是run方法执行的周期;
比如 new Thread()
{
   public void run(){//......}
}.start();




new Thread(new Runnable() {

@Override
public void run() {
while (true) {
//22222222
System.out.println(Thread.currentThread().getName()
+ "thread ...");
}

}
}) {
public void run() {
while (true) {
//1111
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ "haha");
}
}

}.start();
上面这个代码中会执行1111还是2222处的代码呢?
经过分析我们知道,上面的代码可以理解成这样;
子类中覆盖了父类中的方法,执行时当然是找子类的方法;
或者可以理解为是用了直接继承Thread类的方式创建并启动线程;


Thread实现了Runnable接口;因此,实现了Runnable接口的类和Thread是平级的;
Thread牛就牛在了可以启动系统资源来启动一个线程;

多线程技术会提高程序的运行效率吗?那为什么还会有多线程下载呢?
多线程是不会提高程序的运行效率,反而会影响程序执行效率,至于为什么会有多线程下载呢?这是因为我们在下载时;
给用户提供下载的服务器为每个人都分配一定的带宽,如果是单线程,就只分配一份带宽();
而如果是多线程呢就会分配多份带宽,相比之下,一个用户获得了更多的带宽
这就是多线程下载的好处;并不是多线程会使执行效率变高;
---------------------------------------------------------------------------------是这样的吗
多线程实现方式有两种:
1:一种是直接继承Thread类,覆盖该类的run方法,将要被线程调用的代码封装进run方法中;
然后调用start启动线程;
2  还有一种是实现Runnable接口,覆盖该类的run方法。然后创建该实现类的对象,作为实际参数传入Thread类的构造函数。
然后调用start方法启动线程;
用代码来体现就是;
//第一种方式:
new Thread()
{
public void run()
{
//多线程执行代码;
}
}.start();
//2 第二种方式:
new Thread(
new Runnable()
{public void run()
{
//多线程执行代码;
}}
).start();
我们一般建议使用第二种方式,即实现Runnable接口方式,为什么呢?
因为这种方式不会有局限性,因为Java中只支持多实现不支持多继承,一旦该类继承了Thread类,就不能继承其他类,
而我们实现Runnable接口,就没有这个局限性;
线程间通信机制:等待唤醒机制:阻塞队列可以实现,jdk1.5并发库中提供了好多比较实用的操作线程类;

我们判断是否会出现线程安全问题的标准有两个:
1 是否是多线程情况,并访问了共享数据;
2 各线程对共享数据的访问限制所使用的锁是否相同;
即线程的互斥;我们对于线程安全问题是使用了sychronied代码块或者函数封装体对
可能出现问题的代码进行封装,要保证使用的锁是同一个,静态中我们使用的是类的字节码文件对象,非静态中一般用的都是this指针,具体情况可以参照懒汉式单例模式;jdk1.5并发库提供了condition和Lock对象,对sychronized进行替代;


对于多线程需要访问和操作的数据,我们用面向对象的思想思考,既然我们要对访问的数据要用到多线程,
即涉及到多线程的访问安全问题,我们对于这个问题把他考虑下,这个可以归结为我们使用到的数据对象,即我们使用到的数据对象他应该考虑到这个问题,这个我们要
对其进行封装,我们只要使用就可以了,即我们用的时候他已经是安全的,不会出现线程安全问题了;
这里我们用到面向对象的思想考虑,即我们的对象要不要被多线程访问?我们考虑到,这个我们必须要对其进行封装,
我们用的时候就可以不要担心线程安全问题,比如StringBuffer和
StringBuilder,前者是线程安全的,就是其内部对多线程进行了处理;我们使用时无需考虑到线程安全问题,
但是线程安全了,其内部使用到了同步代码块,也就是有锁;每次执行都会有判断锁的操作,大大的影响力程序执行效率,
所以我们推荐使用StringBuilder,虽然其内部并没有解决多线程安全问题,但是程序执行效率高,如果我们要保证线程安全,
可以自己添加同步代码块;来解决;
但是我们在开发程序时要有个这样的思想,就是我们设计一个类时,该类就应该有一些功能,
对外提供他应该提供的方法,解决使用者都要解决的问题,就是说我们使用你这个类没有太多的麻烦需要解决;直接就可以用;
比如集合框架工具类:Collections;


定时器功能
new Timer();






Lock Condition;
JDK1.5并发库中的接口;


Java中的集合框架有两大体系:collection 和map;
Collection 分为两大块:list和set;
list:中元素是可以相同的,有序的,因为集合中有元素的索引;
set:中元素不可以相同,是无序的;


对于集合框架中的Set,Map,List的理解
如果说把List比作是一个单身汉的话,那么他就是一个没有结过婚的单身汉;
而Map是结过婚的,有键值对,是成双成对出现的,虽然键值对有点可以为空,但是这样没有意义,因此不做考虑;
而Set就是离了婚的单身汉,因为他底层使用到的技术就是Map技术,只是把值给去掉,只留下键;

查看Java的源代码:


list和set集合中都有iterator方法;apache-commons包中有扩展的集合,Map
该方法是各集合中的内部类中的方法,由于内部类访问容器中的成员非常容易,而且其对访问方式进行了封装,
外部根本不需要知道,也不能知道其内部的访问方式,同时由于每个集合中都需要有类似的获取元素的方法,
而且每个集合中对访问方式都有不同,所以对这些访问方式进行抽取,形成一个接口,即Iterator接口,
也就是说每个集合中获取元素的访问方式的封装体都是Iterator的实现类,其覆盖了Iterator中的方法,
至于怎么封装的我们无需知道;
对于List集合:当我们使用集合里的方法去操作时,将不能使用迭代器去操作集合,会发生并发修改异常,
因此我们要使用Iterator去迭代时,只能获取和删除,不能修改和添加,但是List的子接口弥补了这一局限;
ListIterator中有添加和修改的方法,但是该接口只对List集合有效;
原因是由于List集合是带角标的;

List有三个子类:
ArrayList:底层是数组数据结构,删除增加慢,但是查找速度快;虽然不是线程安全的,但是Java中有工具提供了解决方案,推荐使用
该集合,当我们不知道要选择什么集合是,ArrayList是首选;
LinkedList:底层数据结构是链表结构,增加删除快,但是查找慢;
对于他,1.6之前,对集合的取出等一系列操作,如果集合为空,这抛出NoSuckElementException;
1.6升级之后对其进行改进,poll×,peek*,offer×等,如果集合为空,他返回的是null,而不抛出异常,大大提高了程序的健壮性;

Vector:底层是数组数据结构,是线程安全的,但是增加和删除,查找都很慢,已经被ArrayList替代;但是他里面有枚举
,是其特有的取出方式;JDK1.0 开始的

Set集合:是无序的,元素不能重复的;底层用的是hash算法来的;
子类----HashSet
保证元素的唯一性是hashCode()的返回值,如果hashSet是相同的,再比较equals方法,


    ----TreeSet
要存入该集合中的元素必须具有可比性,
第一种方式:
即要实现Comparable接口,覆盖compareTo方法,这也叫做元素的自然排序,也叫默认排序;
他保证元素唯一性的方式是compareTo返回0表示相同,返回大于零的数表示大于,返回小于零的数表示小于;
其底层原理是二叉树,左边的是小的,右边的是
大的,他是这样保证元素的有序性,那么他是怎么保证元素的唯一性呢??
        应该也是hashCode方法和equals方法吧;
但是事实并不是如此,直接使用的是compareTo方法,如果compareTo方法返回值为0,则不存入;从而保证了其唯一性;
        那是不是说hashCode和equals方法对于TreeSet没多大作用呢???-----------
List list = new ArrayList();
list.add(33);
list.add(323);
list.add(313);
for (int i : list) {
System.out.println(i);
}
ListIterator listIterator = list.listIterator();
while (listIterator.hasNext()) {
// list.remove(0);
}
for (int i : list) {
System.out.println(i);
//注意这里是遍历不出来的,原因我个人认为是由于访问到了最后一个角标;
//因为我们知道增强for循环底层是用的数组结构,由于上面listIterator.hasNext();已经遍历到结尾了
//所以后面的才遍历不到;
}
--------------------------------------------------注意下上面的这段代码--------???????????



第二种方式:传入一个比较器; 实现Comparator接口;
覆盖其中的compare方法,
需要注意到是其实set集合底层使用的就是Map集合,也就是说Set集合基于Map集合


Map集合的特点:是一个键值对形式的,有两个量,即键和值;
子类:
HashTable:是线程同步的,jdk1.0开始就有,键值都不能为空,效率比较低;底层是hash表数据结构
,存入的元素对象需要覆盖hashCode和equals方法;用作键的对象必须实现 hashCode 方法和 equals 方法。
他有一个子类叫Properties,在操作properties配置文件时很有用;

HashMap:底层是hash表数据结构,键值可以为空,但是这样的做法,意义不大,线程是不同步的;
        他有一个子类LinkedHashMap,需要大量的增加删除数据时,建议使用;
TreeMap:底层是二叉树数据结构(也叫红黑数);键值虽然可以为空,但是意义不大;
        存入其中的数据是按照键排序的,
集合之间是可以嵌套的;集合框架看自己的需求而选取使用;
Collections类是操作集合的工具类,该类中包含了许多实用的方法,如sort方法,还有解决线程同步问题的方法等等
反正只要我们有面向对象的思想,想要什么功能都可以去找方法使用,找不到自己造一个出来;一句话,
面向对象的思想要有;
关于集合与数组间的转换,
数组转集合后有局限,不能使用集合的增加删除方法;
//这是为什么呢?我知道会发生一个不支持这种操作的异常,可理由何在,难道又得去看源代码----------------????
而集合转数组要看实际需求,一般只要我们的集合不需要被增加或删除元素可以转换;









Runtime类可以执行一些字符串指令,他采用单例设计模式;是个比较重要的命令;
他能执行哪些指令呢?比如。。。。

Process:进程类;

Date的替代品,calendar类;
记住要仔细查看这些API文档:
1 集合框架,IO(文件和字符)Socket,多线程,定时器,线程类,正则表达式Pattern;


网络编程:Socket
对于Socket编程,是网络编程范畴,所有网络编程的原理都是基于这个,比如tomcat服务器,IE浏览器,等等网络应用;
这里我们定义一个Socket,不管是服务端还是客户端,都得指定端口号,客户端还得指定主机号,标识你所要访问的主机和该主机的哪个端口,比如tomcat服务器;
他走到是8080端口,http走到是80端口,还有FTP,以及邮件传输协议等等,都是走的端口,其底层都是用的Socket技术,只是对其进行了封装,添加了一些功能;
同时我们需要注意到是Socket偏向于传输层,而我们以后学到web开发,主要是应用层;
关于Socket,分为两大类;UDP和TCP,其区别是
UDP:是面向无连接的不可靠的,数据包不可大于64K,但是执行效率高,同时出错率也高,
Java中使用对UDP提供了DatagramSocket和DatagramPacket;也分为客户端和服务端,因此我们开发时两端都得指定相应的端口号,否则找不到指定的应用程序端口,
就好比不给你地址,你就会迷路;具体的应用程序是      飞秋软件;
QQ:有TCP,也有UDP;

TCP:是面向连接的,可靠的,需要三次握手确定建立连接,安全可靠,但是相应的执行效率也受到影响,一般我们下载都是用的这个TCP,
Java中提供的对象是ServerSocket和Socket,前者是服务端,后者是客户端,同样的需要指定端口,客户端还得指定主机号;
我们知道服务器是给客户端提供服务的,同一时刻有多个客户端连接,那么服务器是怎么解决这一问题的呢?
我们知道Java有多线程机制,因此我们可以使用多线程来实现,即Socket socket=serverSocket.accept();然后将这个socket传入,
将所有客户端都得执行的代码放入run方法中,即可实现多客户端的同时访问,否则单线程是致命的错误;

面向对象的思想一定要有,我们在写程序过程中,如果碰到一些经常用到的功能,或者说碰到什么问题?首先得想想Java有没有给我们提供对象来
解决这些问题?或者说有哪些开源jar包有这些功能,如果有就使用它,没有我们自己造一个,即自己创建一个;
同时对于一些共性代码,或者说经常使用到的做法,我们能否对其进行抽取封装,使我们使用时更方便快捷;


URL和URLConnection是很重要的两个类不管是3G还是web开发都得用到;
Timer可以实现自动启动某项任务;做手机开发需要用到的;
开源的工具可以实现这样的事情:quartz;这个工具貌似还蛮重要的。。。。


所谓将数据绑定到线程上 ,其实就是创建一集合,当然该集合是静态共享的,以线程对象为关键字,而以想要绑定到线程的数据为;
值,取出时,只需提供thread即可;类似于双列集合的存取;ThreadLocal可以实现这一功能;其底层就是用的map集合;



装饰设计模式:举例比如我们要将list集合体系中的元素实现去重复元素,即不可以出现相同的元素;
我们可以对List集合进行封装,底层使用List集合中的方法,只是加入我们去重复元素操作的代码;
这一过程就可以称之为使用了装饰设计模式;


如果我们想把某个类做成是绑定到当前线程的对象;使得使用者在使用时,只要get一下,就可以得到与当前线程绑定的对象;
这是我们根据面向对象的思想考虑得出来的;因为要将某个对象绑定到某个线程上,供他人使用,这个绑定到当前线程和获取绑定当前线程相关的对象;
这些动作只要是有需要的调用者都要用到,为了高类聚的考虑,我们将这个功能加在该类身上,使其被他人使用时就可以获取到与当前线程相关的
对象;这个也可以理解为一个装饰设计模式;

当我们要用多线程去访问共享数据时,容易出现线程安全问题;因此jdk1.5升级以后;
如果该共享数据是基本数据类型时;可以使用AtomicInteger类来解决;



在jdbc中,我们学过由于数据库连接要耗费极大的性能,如内存和执行效率,因此我们对于数据库的连接管理采用数据库连接池来管理;
dbcp和c3p0是这类数据库连接池的优秀代表,其工作的过程是根据当前需要的数据库连接数,以及可能会增加的连接来智能动态的创建
以及摧毁数据库连接,保证数据库连接够用,又不过大多消耗系统的资源;就好比铁道部加开临客和取消部分列车是同样的道理;
因此在多线程中,我们也有类似的技术,称之为线程池;Java给我们提供的类是:Executors,他可以创建一个线程池ExecutorService:
线程池分为固定线程池(newFixedThreadPool(int nThreads) );和缓存线程池以及单一线程连接池;具体可参看相关api文档;
该线程池可创建固定的线程数来完成相应的任务;
使用示例:
ExecutorsService threadPool=Executors.newFixedThreadPool(5);
threadPool.execute(Runnable task);//task表示要被某个线程执行的任务;
线程池还可以和定时器绑定或者说相关;

读写锁可以实现读写数据安全性,同时提高读写速率ReadWriteLock;可以实现这一功能;

Semaphore:信号灯,可以实现几个线程间交互;比如说我们有一个停车场,有三十个停车位,而有40辆车要停,而每当一辆车走时,另一辆车就可以进入停放;
而我们这个信号灯就是显示当前还有几辆车在等着停;

CyclicBarrier:线程集中等待出发机制:比如说多个线程组成一个序列,比如说线程池,这些线程一旦加入到CyclicBarrier的序列时,就同进退;
只有全部成员都到齐之后才可以一起出发,谁先到先等到;
CountDownLatch:可以实现一个线程给多个线程发布命令,或者说只有多个线程同时到时才能发表命令;

Exchanger:实现两个线程之间数据的交换,好比两个人去接头卖毒品,一个带着钱去换毒品,一个带着毒品去换钱,但是他们其中某个人先到,先到了先等,另一个到了,
马上交换,交换后就个回各家;

可阻塞队列ArrayBlockQueue

集合的并发访问:java.util.concurrent包中;提供了线程安全的集合;

集合的并发访问容易出现线程安全问题,Collections中有方法可以解决线程安全问题,其原理是,底层仍然调用这些集合的方法
只是在调用每个方法时都加上同步锁从而保证各方法之间的互斥;这也可以算是装饰设计模式;




HashSet和HashMap,其实HashSet底层用的就是HashMap

public class MyProxyFactory implements InvocationHandler {
private Object targetObject;// -------------A

public Object createProxyInstance(Object targetObject)//-------------B
{
this.targetObject = targetObject;//------------------C
return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),//-----D
this.targetObject.getClass().getInterfaces(), //----------E
this);//----------------H
}

@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
BusinessServiceImpl service=(BusinessServiceImpl)this.targetObject;//------------F
Object result=null;
if(service.getUser()!=null){
result=method.invoke(this.targetObject, args);//---------------G
}
return null;
}
}
动态代理解析:
A:该处的targetObject,表示的是你要为哪个对象创建代理,即你要我代理,你要我代理什么啊,你得给我些东西我才知道你要我代理什么东西;
B:通过Proxy的newProxyInstance方法,他需要一个参数,由调用者传递,就是要我代理的对象是哪一个,我会把你传进来的对象赋值给targetObject;
C:将传递来的对象赋值给目标对象;他们两个其实是一样的,一个是形式参数,一个是接受者;
D:Proxy代理类的newProxyInstance()方法的第一个参数是需要目标类的类装载器;
E:newProxyInstance()方法的第二个参数是目标对象所实现的所有接口;
H:第三个参数是一个回调对象;由于本类是一个代理工厂,他实现了回调接口InvocationHandler;并实现了其invoke方法;因此this代表着一个回调对象;
回调的实现就是靠该对象;
F:将传递进来的目标对象强转为其原始类型,因为只有他原始的类型对象中才有参数来判断要不要执行某方法,代理对象要根据他提供的拦截条件来拦截;
而newProxyInstance接受的是一个Object对象;当然也可以定义泛型;

java5新特性中的可变参数底层用到的是一个数组,因此我们可以将该可变参数用数组的方式操作;
享元设计模式:
Integer var1=122;
Integer var2=122;
上面这两个是同一个对象;
Integer var3=134;
Integer var4=134;
上面这两个不是同一个对象;
上面var1和var2用的是享元设计模式;把在一个字节范围的数字缓存起来;flyweight;

之所以Java中不需要将if语句中if(null==user)来防止将其写成if(user=null)
是因为Java的判断语句中真假值只有false和true,而c++中非0就是真;


类的字节码文件对象使用的类装载器不同,那么他们就是两个不同的类,
相互转换的时候会发生类型转换异常。

用char数组存多个中文字符 比用string好在哪?
既然可以用一个Java类去模拟一个枚举,可不可以说枚举的底层实现原理就是用的普通Java类
String类是一个final类,不能被继承;
String str0="java";
String str1=new String("hello java");
第一中是一个引用指向了常量区中的一个常量:"java";
而第二个是这样的,hello java仍然在常量区中,而new String():在堆内存中
他将常量区的 hello java复制到堆内存中;而str实际指向的是堆内存中的那个hello java
因此说,使用字符串用第一种方式比较优雅;

一个引用占4byte ,在32位系统上 

Java中后置自加要到语句结束后改变值;比如
int count = 0;
        for (int i = 10; i <= 10; i++)
        {
        count=count++;
        }
        System.out.println(count);
输出结果是0;
可以这样理解:
int count = 0;
        for (int i = 10; i <= 10; i++)
        {
int temp=count++;//这里temp为0;
        count=temp;
        }
        System.out.println(count);

在apache-commons-collections包中;
有BidiMap,MapIterator等使用的数据结构和类;

类加载器的委托机制:先找基类,一级级的往下找;
但是也可以自己写一个类加载器,自己加载某个类;

模板方法设计模式:
父类--->loadClass,这是所有子类都得干的事,也就是说,先得找父类。
即类加载器的委托机制;

也就是说所有子类共有的东西放到父类中。而每个子类不同的功能都供
子类去实现;
java中类加载器,分为三个级别:
BootStrap:嵌套在JavaVM内核中的一段有c++语言写出来的二进制代码,负责加载 jre/lib/rt.jar中的类;
ExtClassLoader:用来加载jre下的/lib/ext/目录中的类;---他能用来加载资源文件吗?
AppClassLoader:用来加载classpath下的lib目录和资源;
委托加载机制:在java中,我们写的一个普通类,放在classpath目录下;
JVM在加载的时候,首先找AppClassLoader,,然后AppClassLoader将请求转发
去找他的上级ExtClassLoader:ExtClassLoader收到请求后,也是转发给他的上级:
BootStrap;由于BootStrap是顶级类加载器,他不能在转给上级,就只好在他的管辖范围内找
即rt.jar中找,找到就加载,如果没找到,继续转到下级,即ExtClassLoader;然后ExtClassLoader去自己的管辖范围中去找,即ext目录中找;找到就加载,没找到就继续转到下级,
即AppClassLoader,他也是到自己的管辖范围中去找,找到就返回找不到,他就不能在转给下级了,因为他可能有多个子类,所以就直接抛异常,即ClassNotFoundException;
这样的委托机制有利于集中管理;


因此我们是不能写一个java.lang.System类去替代jdk中的那个类的
原因就是类加载器的委托加载机制;但是我们可以自己去写一个类加载器,撇开类加载器的委托
加载机制;覆盖loadClass方法;
在类加载的时候,如果A类中引用了B类,
如果A类用的是哪个类加载器,那么B类也是用哪个类加载器进行加载;

有包名的类,不能调用无包名的类;
classpath:是指的什么;
PrintWriter中的write(String str);方法
没有对Str进行null校验,因此容易发生空指针异常,使用时尤其要注意;


静态方法不能在子类中被重写
抽象方法不能是静态的:
原因:因为静态方法不能在子类中被重写;其次静态方法可以直接用类名调用,而抽象方法是不能被调用的;
abstract不能与final并列修饰同一个类
方法加载的时候创建局部变量,不是,执行的时候;
条件运算符会将变量进行升级.三目运算符中:第二个表达式和第三个表达式中如果都为基本数据类型,整个表达式的运算结果
由容量高的决定。99.9是double类型 而9是int类型,double容量高。

 constructor必须与class同名,方法可以和class同名,但是不建议这么干;

abstract:不能修饰字段;
对于动态代理的理解;
这里有三个牵涉的对象;
1 需要被代理的对象--目标对象;---Information类,假设该类中有一个printInfo();方法;
2 代理对象ProxyInstance--被JVM动态生成的和目标对象在同一个继承体系中的对象;因此创建一个代理对象时需要传递和目标对象
   共同的类装载器和,代理对象实现的接口,就是告诉虚拟机,你在给我动态创建代理对象时,记得给我实现目标对象实现的所有接口;
    并用目标对象使用的类加载器来加载我的类;
3 处理代理请求的类----代理请求处理类,即一个InvocationHandler;这个类为目标对象targe和代理对象,proxy进行牵线搭桥;


现在我想计算下printInfo方法执行所需要的时间,并且是我对外提供的;那么你可以用动态代理去实现;
过程如下;创建个Information的实现对象;把他当做目标类,调用Proxy的静态方法;并传递三个参数;
1 Information类使用的类装载器,通过 information.getClass().getClassLoader();获取;
2 Information类实现的所有接口,通过information.getClass().getInterfaces();获取;
3 以匿名内部类的形式创建InvocationHandler的实例对象;实现invoke方法;
  在invoke方法内部,也有三个参数,第一个Object proxy--代理对象,proxy对象身上的method对象,表示当前执行到的方法(其实
  代理对象ProxyInstance和Information对象都实现了相同的类,有相同的方法;)
  在执行这个方法之前,你可做一些操作,或者在执行这个方法之后你也可以做一些操作;比如;
  advice.doBefore();
  Object reVal=method.invoke(args);
  advice.doAfter();
  return reVal;
  这个advice对象是你传进来的需要在执行目标对象的方法(当然也可以对某些特定的方法进行拦截)之前,之后需要做的操作;
  这个对象中封装了你需要执行的操作;
  执行完成后返回值给代理对象执行的方法;
也就是说你一旦创建好了一个代理对象,并将你需要插入的公共业务逻辑代码以对象参数的形式传入的时候;
Proxy.newProxyInstance();给你返回了一个目标对象的代理对象;
当你每次调用代理对象的某个方法,在这个方法执行前后都加入了你需要插入的公共逻辑代码;

类装载器就是用的这种单例模式;
构造函数保护;--

如果你想使得一个类不能被new,而且又不能被继承;
不能被new,即使在本类中也不能, 说明他是一个抽象类;--
不能被继承,说明他是一个final类;
但是abstract 和final在修饰类的时候不能同时存在,那么怎么办呢?
将该类定义为abstract,即抽象,因此不能被new;
同时将所有构造方法私有化,这样就会隐式的将一个类变为final类,而不能被其他类继承;
因为如果子类在初始化的时候必须调用父类的构造函数进行初始化,而由于父类中的构造函数都是私有的
不可见,因此不能继承;
另外一个理由:子类的默认构造函数的第一行会调用父类的默认构造函数;由于父类的默认构造函数私有,因此也不行;
Ognl类就是这么个工具类,不能被继承,也不能被new,但是方法都是静态的;这个方法我们可以借鉴;

hashMap的构造函数中接收一个指定容量大小的构造函数;
不指定初始容量大小,默认为16个;
java.util.AbstractMap:这个类是对map接口中不涉及到数据结构的方法都进行了实现
对涉及到数据结构的方法要么私有,要么在方法中直接抛出异常,也就是说子类必须去实现它;
这是一种模板方法设计模式;
public V put(K key, V value) {
throw new UnsupportedOperationException();
}
public abstract Set> entrySet();
如果你同时实现了Map接口,又继承了AbstractMap:
那么你只需要覆盖实现AbstractMap中的抽象方法即可;
我的疑问是为什么他还要实现Map接口;
















你可能感兴趣的:(java基础)