java基础知识(一)

  • java中的访问权限有public,protected,private,default,default不能修饰变量。被定义为abstract的类需要被子类继承,但是被修饰为final是不能被继承和重写的。

  • final的对象的引用不能指向不同的对象,但是final对象里的数据可以改变。
  • HashMap和Hashtable:HashMap实际上是一个“链表散列”的数据结构,即数据和链表的结合体。HashMap底层结构是一个数组,数组中的每一项是一条链表。HashMap的实例有两个参数影响其性能“初始容量”和“装填因子”。HashMap线程不安全,Hashtable线程安全。HashMap中的key-value都是存储在Entry中。Hashtable键和值不允许出现null的情况,HashMap可以存null键和null值,这样的键只有一个,值可以有多个,在判断其中是否含有某个值,应该用containsKey()方法来判断。解决冲突主要有三种方法,定址法,拉链法,再散列法,HashMap是通过拉链法来解决哈希冲突的。HashMap和Hashtable他们的继承不同。哈希值的使用不同,Hashtable直接使用对象hashCode,而HashMap从新计算hash值。HashMap的初始数组大小16,增加方式是2的指数,Hashtabel的初始数组大小11,增加方式是old*2+1.继承是不同的Hashtable继承Directory,HashMap继承AbstractMap

  • 从action类上分析:Struts1要求Action 类继承一个抽象基类,Struts1的一个普遍问题是使用抽象类编程而不是接口。Struts2 Action类可以实现一个Action接口,也可实现其他接口,Struts2提供了一个ActionSupport基类来实现常用的接口,Action接口不是必须的,任何有execute标识的POJO对象都可以用作Struts2的Action接口。从Servlet依赖分析:Sturts1 Action依赖Servlet API,因为当一个Action被调用时HttpServeltRequest和HttpServeltResponse被传递给execute方法。Struts2 Action不依赖容器,允许Action脱离容器单独被测试,如果需要,Strutes2 Action可以访问初始的request和response。action线程模式分析:Struts1 Action是单例模式并且必须是线程安全的,因为仅有Action的一个实例来处理所有的请求。Struts2 Action 对象为每一个请求产生一个实例,因此没有线程安全问题。

  • Ant和Maven都是基于java的构建工具,Ant的特点:没有一个约定的目录结构,必须明确让ant做什么,什么时候做,然后编译,打包没有生命周期,必须定义目标及其实现的任务系列,没有集成依赖管理。Maven有约定,知道你的代码在哪里,放到哪里去,拥有生命周期,比如maven install 就可以自动的执行编译,测试,打包等,拥有依赖管理,仓库管理。

  • 构造方法是一种特殊的方法:构造方法的方法名必须与类名相同。构造方法没有返回值类型,也不能定义为void,在方法名前不声明方法类型。构造方法的主要作用是完成对象的初始化工作,它能够把定义对象时的参数传给对象域。一个类可以定义多个构造方法,如果定义类时没有定义构造方法,则编译系统会自动插入一个无参数的默认构造器,这个构造器不执行任何代码。构造方法可以重载,以参数的个数,顺序,类型。

  • java类加载器引导类加载器(bootstrap class loader)它用来加载java的核心库,是用原生代码实现的。扩展类加载器(extensions class loader)它用来加载java的扩展库。系统类加载器(system class loader)它根据java应用的类路径来加载java类。tomcat为每个app创建一个loader,里面保存着此WebApp的ClassLoader。需要加载WebApp下的类时,就取出ClassLoader来使用。

  • 异常继承的图

     

  • 这是Struts项目与MVC模式图

  • J2EE中常用的名词解释EJB容器,Enterprise java bean容器,他提供给运行在其中的组件EJB各个管理功能。JNDI(java naming&directory interface)java命名目录服务,提供一个目录系,让其他各地的应用程序在其上留下自己的索引,从而满足快速查找和定位分布式应用程序的功能。JMS(java message service)java消息服务,主要实现各个应用程序之间的通讯,包括点对点和广播。JTA(java transaction api)java事物服务,提供各种分布式事务服务,应用程序只需要用其提供的接口即可。JAF(java action framework)java安全认证服务,提供一些安全控制方面的框架,让开发者通过各种部署和自定义实现自己的个性安全控制策略。RMI/IIOP(remote method invocation/internet)主要用于通过远程调用服务。

  • 用new创建的对象存放在堆区。函数中的临时变量在栈区。java中的字符串在字符串常量区

  • 从地址栏显示来说:forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容在发给浏览器,浏览器根本不知道服务器发送的内容从什么地方来,所以他的地址还是原来的地址。rediredt是服务器根据逻辑发送一个状态码,告诉浏览器重新请求那个地址,所以地址栏显示的是新的URL。从数据共享来说:forward转发页面和转发到的页面可以共享request里面的数据,redirect不共享数据。从运用角度来说:forward一般用于用户登录的时候,根据角色转发到相应的模块,redirect一般用于用户注销登录时用户返回主页面和跳转到其他网站等。从效率上来说:forward高,redirect低。

  • 依赖注入和控制反转是对同一件事情的不同描述,从某个方面讲,就是他们描述的角度不同,依赖注入是从应用程序的角度描述,应用程序依赖容器创建并注入它所需要的外部资源;而控制反转是从容器的角度在描述,容器控制应用程序,由容器反向向应用程序注入应用程序所需要的外部资源。

  • 复制数组方法效率:System.arraycopy>clone>Arrays.copyOf>for循环
  • 线程sleep()和yeild()有什么区别?(1)sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级线程运行机会,yeild()只会给相同或者更高优先级的线程以运行的机会。(2)线程执行sleep()方法后转入阻塞状态,而执行yield()方法后会进入就绪状态。(3)sleep()方法声明抛出InterruptException异常,而yield不会抛出异常。(4)sleep()比yield()方法有更好的移植性。
  • java中常见线程有四种方法:继承Thread;实现接口Runable;实现Callable接口通过FutureTask包装器来创建Thread线程;使用ExecutorService、Callable、Future来实现有返回值的线程;前两种是没有返回值的;后两种是带有返回值的;
    //这是实现Callable接口用FutureTask包装器来实现线程
    Callable<V> oneCallable = new SomeCallable<V>();   
    //由Callable<Integer>创建一个FutureTask<Integer>对象:   
    FutureTask<V> oneTask = new FutureTask<V>(oneCallable);   
    //注释:FutureTask<Integer>是一个包装器,它通过接受Callable<Integer>来创建,它同时实现了Future和Runnable接口。 
      //由FutureTask<Integer>创建一个Thread对象:   
    Thread oneThread = new Thread(oneTask);   
    oneThread.start();   
    //至此,一个线程就创建完成了。
    import java.util.concurrent.*;  
    import java.util.Date;  
    import java.util.List;  
    import java.util.ArrayList;  
      
    /** 
    * 有返回值的线程;使用ExecutorService、Callable、Future来实现多线程 
    */  
    @SuppressWarnings("unchecked")  
    public class Test {  
    public static void main(String[] args) throws ExecutionException,  
        InterruptedException {  
       System.out.println("----程序开始运行----");  
       Date date1 = new Date();  
      
       int taskSize = 5;  
       // 创建一个线程池  
       ExecutorService pool = Executors.newFixedThreadPool(taskSize);  
       // 创建多个有返回值的任务  
       List<Future> list = new ArrayList<Future>();  
       for (int i = 0; i < taskSize; i++) {  
        Callable c = new MyCallable(i + " ");  
        // 执行任务并获取Future对象  
        Future f = pool.submit(c);  
        // System.out.println(">>>" + f.get().toString());  
        list.add(f);  
       }  
       // 关闭线程池  
       pool.shutdown();  
      
       // 获取所有并发任务的运行结果  
       for (Future f : list) {  
        // 从Future对象上获取任务的返回值,并输出到控制台  
        System.out.println(">>>" + f.get().toString());  
       }  
      
       Date date2 = new Date();  
       System.out.println("----程序结束运行----,程序运行时间【"  
         + (date2.getTime() - date1.getTime()) + "毫秒】");  
    }  
    }  
      
    class MyCallable implements Callable<Object> {  
    private String taskNum;  
      
    MyCallable(String taskNum) {  
       this.taskNum = taskNum;  
    }  
      
    public Object call() throws Exception {  
       System.out.println(">>>" + taskNum + "任务启动");  
       Date dateTmp1 = new Date();  
       Thread.sleep(1000);  
       Date dateTmp2 = new Date();  
       long time = dateTmp2.getTime() - dateTmp1.getTime();  
       System.out.println(">>>" + taskNum + "任务终止");  
       return taskNum + "任务返回运行结果,当前任务时间【" + time + "毫秒】";  
    }  
    }
    
  • 集合关系图
  • JVM加载类的方式,我们称为“双亲委托模式”:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委托给自己的父类加载器,每一层的类加载器都是如此,因此所有的类加载请求最终都应该传送到顶层Bootstrap ClassLoader中,只有当父加载类无法完成加载请求时,子加载类才会尝试自己加载。“双亲委托模式”的最重要的用途是为了解决类载入过程中的安全性问题

  • jvm中划分三个代:年轻代(Young Generation),年老代(Old Generation),持久代(Permanent Generation)。年轻代:所有新生成的对象首先都放到年轻代,年轻代的目标是尽快收集那些生命周期比较短的对象,年轻代一般分为三个区域,一个Eden区,两个Survivor区,大部分对象在Eden区生成,当Eden区满时,还存活的对象将被复制到两个中的一个Survivor区,当这个Survivor区满时,此区的存活对象将被复制到另一个Survivor区,当这个区也满了,那么将从第一个Survivor区复制过来的目前还存活的对象将被复制到“年老区(Tenured)”。老年代:在年轻代中经过N次垃圾回收仍然存活的对象,就会被放到年老代。持久代:用于存放静态文件,如java类,方法等。GC的两种类型:Scavenge GC:一般情况下,当新对象生成,并且在Eden申请空间失败时,就会触发Scavenge GC,对Eden区域进行GC,清除非存活对象,并且把存活的对象转移到Survive区,然后整理Survive两个区。这种方式的GC是对年轻代的Eden区进行,不会影响到年老代。Full GC:对整个堆进行整理,因为对整个堆进行整理所以比Scavenge GC要慢,因此应该尽可能减少Full GC的次数。可能造成Full GC的原因:年老代被写满;持久代被写满;System.gc()被显示调用;通过Minor GC后进入年老代的平均大小大于年老代的可用内存;由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转移到年老代,且年老代的可用内存小于该对象大小。GC算法:标记清除算法;复制算法;标记整理算法。Minor GC:从年轻代空间回收内存被称为Minor GC,当Eden区没有足够的空间时,会发起一次Minor GC,将Eden区中存活的对象转移到Survivor区。Major GC:是清除年老代区域,当升到年老代的对象大于年老代剩余空间时会发生Miajor GC,发生Major GC时用户线程会暂停,会降低系统性能和吞吐量。java中一般不手动触发GC,但可以用不同的引用类来辅助垃圾回收的工作(比如:弱引用或软引用)。引用类:强引用;虚引用;弱引用;软引用。
  • jvm内存划分:一共分为五个块,其中线程共享区域为:java堆,方法区;线程私有区域为:jvm栈,本地方法栈,程序计数器。其中java堆包含了jvm的三代划分。

  • 匿名内部类:注意如下几点:1.由于匿名内部类不能是抽象类,所以它必须实现它的抽象父类或者接口里面的所有抽象方法,使用匿名内部类时,必须是继承一个类或者实现一个接口。2.匿名内部类中不能有构造函数的。3.匿名内部类中不能存在任何的静态成员变量和静态方法。4.匿名内部类是局部内部类,所以局部内部类的所有限制同样对它也有效。当所在的方法的形参需要被内部类里面使用时,该形参必须是被final修饰。
  • 结构模型模式