Java日记2018-07-02

2018-07-02  本文已影响0人  hayes0420
  1. 丑数
//49. 丑数
    public static int ugly(int n){
        if(n<1) return 0;
        int[] arr=new int[n];
        int index2=0;
        int index3=0;
        int index5=0;
        arr[0]=1;
        int min=0;
        for(int i=1;i<n;i++){
            //丑数的计算是通过动态规划的方法,arr[i]是前一个素数与2 3 5的因子的乘积的最小值获取。一定一定要注意是乘积
            min = min(arr[index2]*2,arr[index3]*3,arr[index5]*5);
            arr[i]=min;
            while(arr[index2]*2<min) index2++;
            while(arr[index3]*3<min) index3++;
            while(arr[index5]*5<min) index5++;
        }
        System.out.println(arr[n-1]);
        return arr[n-1];
    }
  1. 第一个只出现一次的字符位置
    这题也简单,使用整数数组的值是否等于1判断是否有重复值。
    有个有意思的地方是初始化数组英国选取多少?这个时候就了解基本数据类型占有大小
    Byte 1 个字节
    short 2个字节
    int 4个字节
    long 8个字节
    float 4个字节
    double 8个字节
    Boolean 1个字节 (前7位是0)
    char 2个字节 采用unicode编码,它的前128字节编码与ASCII兼容
    所以初始化时候用的是int[] cnt = new int[256]
public static int findonce(String str){
        if(str==null) return -1;
        int[] cnt = new int[256];
        Arrays.fill(cnt, 0);
        for(int i=0;i<str.length();i++) {
            cnt[str.charAt(i)]++;
        }
        for (int i = 0; i < str.length(); i++)
            if (cnt[str.charAt(i)] == 1){
                System.out.println(i);
                 return i;
            }
               
        return -1;
    }
  1. 二叉搜索树的第 K 个结点
//54. 二叉搜索树的第 K 个结点
    private static int cnt=0;
    private static TreeNode res ;
    public static TreeNode KthNode(TreeNode root,int k){
        inorder(root,k);
        return res;
    }
    public static void inorder(TreeNode root,int k){
        if(root==null|| cnt>=k) return;
        inorder(root.left,k);
        cnt++;
        System.out.println("root:"+root.val+" cnt:"+cnt);
        if(cnt==k) res=root;
        inorder(root.right,k);
    }
  1. 加载

加载是类加载的一个阶段,注意不要混淆。

加载过程完成以下三件事:

通过一个类的全限定名来获取定义此类的二进制字节流。
将这个字节流所代表的静态存储结构转化为方法区的运行时存储结构。
在内存中生成一个代表这个类的 Class 对象,作为方法区这个类的各种数据的访问入口。
其中二进制字节流可以从以下方式中获取:

从 ZIP 包读取,这很常见,最终成为日后 JAR、EAR、WAR 格式的基础。
从网络中获取,这种场景最典型的应用是 Applet。
运行时计算生成,这种场景使用得最多得就是动态代理技术,在 java.lang.reflect.Proxy 中,就是用了 ProxyGenerator.generateProxyClass 的代理类的二进制字节流。
由其他文件生成,典型场景是 JSP 应用,即由 JSP 文件生成对应的 Class 类。
从数据库读取,这种场景相对少见,例如有些中间件服务器(如 SAP Netweaver)可以选择把程序安装到数据库中来完成程序代码在集群间的分发。

2. 验证

确保 Class 文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。

3. 准备

类变量是被 static 修饰的变量,准备阶段为类变量分配内存并设置初始值,使用的是方法区的内存。

实例变量不会在这阶段分配内存,它将会在对象实例化时随着对象一起分配在 Java 堆中。(实例化不是类加载的一个过程,类加载发生在所有实例化操作之前,并且类加载只进行一次,实例化可以进行多次)

初始值一般为 0 值,例如下面的类变量 value 被初始化为 0 而不是 123。

public static int value = 123;

如果类变量是常量,那么会按照表达式来进行初始化,而不是赋值为 0。

public static final int value = 123;

4. 解析

将常量池的符号引用替换为直接引用的过程。

5. 初始化

初始化阶段才真正开始执行类中的定义的 Java 程序代码。初始化阶段即虚拟机执行类构造器 <clinit>() 方法的过程。

在准备阶段,类变量已经赋过一次系统要求的初始值,而在初始化阶段,根据程序员通过程序制定的主观计划去初始化类变量和其它资源。

  1. 类加载器分类
    启动类加载器(Bootstrap ClassLoader)此类加载器负责将存放在 <JAVA_HOME>\lib 目录中的,或者被 -Xbootclasspath 参数所指定的路径中的,并且是虚拟机识别的(仅按照文件名识别,如 rt.jar,名字不符合的类库即使放在 lib 目录中也不会被加载)类库加载到虚拟机内存中。启动类加载器无法被 Java 程序直接引用,用户在编写自定义类加载器时,如果需要把加载请求委派给启动类加载器,直接使用 null 代替即可。

扩展类加载器(Extension ClassLoader)这个类加载器是由 ExtClassLoader(sun.misc.Launcher$ExtClassLoader)实现的。它负责将 <JAVA_HOME>/lib/ext 或者被 java.ext.dir 系统变量所指定路径中的所有类库加载到内存中,开发者可以直接使用扩展类加载器。

应用程序类加载器(Application ClassLoader)这个类加载器是由 AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的。由于这个类加载器是 ClassLoader 中的 getSystemClassLoader() 方法的返回值,因此一般称为系统类加载器。它负责加载用户类路径(ClassPath)上所指定的类库,开发者可以直接使用这个类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器

  1. 自定义类加载器实现

FileSystemClassLoader 是自定义类加载器,继承自 java.lang.ClassLoader,用于加载文件系统上的类。它首先根据类的全名在文件系统上查找类的字节代码文件(.class 文件),然后读取该文件内容,最后通过 defineClass() 方法来把这些字节代码转换成 java.lang.Class 类的实例。

上一篇 下一篇

猜你喜欢

热点阅读