【Java】基本数据类型

Java基本数据类型一共有八种,引用一下菜鸟教程上的介绍:

byte:
byte 数据类型是8位、有符号的,以二进制补码表示的整数;
最小值是 -128(-2^7);
最大值是 127(2^7-1);
默认值是0;
byte 类型用在大型数组中节约空间,主要代替整数,因为 byte 变量占用的空间只有 int类型的四分之一;
例子:byte a = 100,byte b = -50。


short:
short 数据类型是 16 位、有符号的以二进制补码表示的整数 最小值是 -32768(-2^15);
最大值是 32767(2^15 - 1);
Short 数据类型也可以像 byte 那样节省空间,一个short变量是int型变量所占空间的二分之一;
默认值是 0;
例子:short s = 1000,short r = -20000。


int:
int 数据类型是32位、有符号的以二进制补码表示的整数;
最小值是 -2,147,483,648(-2^31);
最大值是 2,147,483,647(2^31 - 1);
一般地整型变量默认为 int 类型;
默认值是 0 ;
例子:int a = 100000, int b = -200000。


long:
long 数据类型是 64 位、有符号的以二进制补码表示的整数;
最小值是 -9,223,372,036,854,775,808(-2^63);
最大值是 9,223,372,036,854,775,807(2^63 -1);
这种类型主要使用在需要比较大整数的系统上;
默认值是 0L;
例子: long a = 100000L,Long b = -200000L。
“L”理论上不分大小写,但是若写成”l”容易与数字”1”混淆,不容易分辩。所以最好大写。


float:
float 数据类型是单精度、32位、符合IEEE 754标准的浮点数;
float 在储存大型浮点数组的时候可节省内存空间;
默认值是0.0f;
浮点数不能用来表示精确的值,如货币;
例子:float f1 = 234.5f。


double:
double 数据类型是双精度、64 位、符合IEEE 754标准的浮点数;
浮点数的默认类型为double类型;
double类型同样不能表示精确的值,如货币;
默认值是 0.0d;
例子:double d1 = 123.4d。

boolean:
boolean数据类型表示一位的信息;
只有两个取值: true 和 false;
这种类型只作为一种标志来记录 true/false 情况;
默认值是 false;
例子:boolean one = true。


char:
char类型是一个单一的 16 位 Unicode 字符;
最小值是 \u0000(即为0);
最大值是\uffff(即为65,535);
char 数据类型可以储存任何字符;
例子:char letter = ‘A’;。


抱着作死的心理,想了解一下它们的工作原理。

然而找了许久定义八种数据类型的类,除了他们的包装类,什么都没找到。

扫描二维码关注公众号,回复: 186440 查看本文章

最后在百度知道里找到了一条回答:【原问题地址】

基本类型是内置到编译器实现中的,属于“关键字”的范畴,不用头文件支持。而像编译器拓展类型,如__int32,__int64和具体的编译器相关,有些是放在头文件,有些也是内置到编译器中(比如版本较新的VS),也不用手动#include。而BOOL这种类型,就要手动引入头文件。总之不同编译器情况不同。

回答中提到了基本数据类型是关键字(好像的确曾经在关键字表看到过,却没想这么深),由编译器来搞定的,那我先姑且一试。

Java的编译器是javac,那我现在就去找一下javac的源码看一下。

在JDK的安装目录下有一个src.zip,这个就是JDK的源码包,javac的源码在com.sun.tools.javac中。
解压src.zip,进入com.sun,并没有tools这个文件夹。

原来,jdk有两个版本,一种是Oracle jdk,就是我们一般从Oracle官网下载的,它里面并没有完整版的源码包,而完整版的源码包由另一个版本,也就是Open jdk携带,除了一小部分机密的源代码之外,其余都有提供。

Open jdk的官网:http://openjdk.java.net/

安装方式好像只有Linux的,这类我比较懒得折腾,就想到了用OpenSusu去获取,令人兴奋的试OpenSuse自带的jdk就是Open jdk,而且通过OpenSuse的安装与管理程序YaST2也可以很轻松地获取最新版的Open jdk。

jdk1.8的源码压缩包一共49.01MB。

源码阅读


文件夹下有一个Main.java,这应该就是javac的入口函数,这个类下文称为Main类。

Main.java里的函数只有三个,一个main,一个compile,以及一个compile函数的重载形式。

package com.sun.tools.javac;

import java.io.PrintWriter;

/**
 * The programmatic interface for the Java Programming Language
 * compiler, javac.
 */
@jdk.Exported
public class Main {

    /** Main entry point for the launcher.
     *  Note: This method calls System.exit.
     *  @param args command line arguments
     */
    public static void main(String[] args) throws Exception {
        System.exit(compile(args));
    }

    /** Programmatic interface to the Java Programming Language
     * compiler, javac.
     *
     * @param args The command line arguments that would normally be
     * passed to the javac program as described in the man page.
     * @return an integer equivalent to the exit value from invoking
     * javac, see the man page for details.
     */
    public static int compile(String[] args) {
        com.sun.tools.javac.main.Main compiler =
            new com.sun.tools.javac.main.Main("javac");
        return compiler.compile(args).exitCode;
    }



    /** Programmatic interface to the Java Programming Language
     * compiler, javac.
     *
     * @param args The command line arguments that would normally be
     * passed to the javac program as described in the man page.
     * @param out PrintWriter to which the compiler's diagnostic
     * output is directed.
     * @return an integer equivalent to the exit value from invoking
     * javac, see the man page for details.
     */
    public static int compile(String[] args, PrintWriter out) {
        com.sun.tools.javac.main.Main compiler =
            new com.sun.tools.javac.main.Main("javac", out);
        return compiler.compile(args).exitCode;
    }
}

流程很简单,main函数调用compiler函数,运行结束之后退出。

参数String[] args就是用户输入的命令行参数

还是看下compiler函数,首先new了一个com.sun.tools.javac.main.Main(“javac”),然后我有一个大胆的想法,这个参数”javac”应该就是正式调用javac编译器。

看一下com.sun.tools.javac.main.Main类(这个类下文简单称为main.Main类)的构造函数:

 /**
     * Construct a compiler instance.
     */
    public Main(String name) {
        this(name, new PrintWriter(System.err, true));
    }

    /**
     * Construct a compiler instance.
     */
    public Main(String name, PrintWriter out) {
        this.ownName = name;
        this.out = out;
    }

这里也看到了之前compiler重载函数的差别只是引入一个外部的PrintWriter,其余并无两样。

回过头继续看Main类,new好main.Main类之后,便调用了它的compile(String[] args)函数

    /** Programmatic interface for main function.
     * @param args    The command line parameters.
     */
    public Result compile(String[] args) {
        Context context = new Context();
        JavacFileManager.preRegister(context); // can't create it until Log has been set up
        Result result = compile(args, context);
        if (fileManager instanceof JavacFileManager) {
            // A fresh context was created above, so jfm must be a JavacFileManager
            ((JavacFileManager)fileManager).close();
        }
        return result;
    }

Context类,这名字直白的翻译过来是环境、上下文的意思,位于com.sun.tools.javac.util包下,下文简称util.Context类。

看了util.Context的介绍:

/**
 * Support for an abstract context, modelled loosely after ThreadLocal
 * but using a user-provided context instead of the current thread.
 *
 * <p>Within the compiler, a single Context is used for each
 * invocation of the compiler.  The context is then used to ensure a
 * single copy of each compiler phase exists per compiler invocation.
 *
 * <p>The context can be used to assist in extending the compiler by
 * extending its components.  To do that, the extended component must
 * be registered before the base component.  We break initialization
 * cycles by (1) registering a factory for the component rather than
 * the component itself, and (2) a convention for a pattern of usage
 * in which each base component registers itself by calling an
 * instance method that is overridden in extended components.  A base
 * phase supporting extension would look something like this:
 */

感觉这个类的主要功能就是加载编译器所需要的上下文环境,从哪儿得知呢?由用户提供。

关于Context的理解,这篇提问比较说的明白:https://www.zhihu.com/question/26387327

然后就是用JavacFileManager把Context里涉及到的文件注册一下,应该就是把这些文件的信息全丢进JavacFileManager,用JavacFileManager来管理。

接着Result是一个枚举类型,包含在main.Main类中,看了下应该是用来表明编译是否通过,或者发生了哪类错误,错误不会很详细,只是说一下是大致。

    /** Result codes.
     */
    public enum Result {
        OK(0),        // Compilation completed with no errors.
        ERROR(1),     // Completed but reported errors.
        CMDERR(2),    // Bad command-line arguments
        SYSERR(3),    // System error or resource exhaustion.
        ABNORMAL(4);  // Compiler terminated abnormally

        Result(int exitCode) {
            this.exitCode = exitCode;
        }

        public boolean isOK() {
            return (exitCode == 0);
        }

        public final int exitCode;
    }

compile函数有额外三个重载形式,一个接着一个调用:

    public Result compile(String[] args, Context context) {
        return compile(args, context, List.<JavaFileObject>nil(), null);
    }

    /** Programmatic interface for main function.
     * @param args    The command line parameters.
     */
    public Result compile(String[] args,
                       Context context,
                       List<JavaFileObject> fileObjects,
                       Iterable<? extends Processor> processors)
    {
        return compile(args,  null, context, fileObjects, processors);
    }

    public Result compile(String[] args,
                          String[] classNames,
                          Context context,
                          List<JavaFileObject> fileObjects,
                          Iterable<? extends Processor> processors)
    {
        context.put(Log.outKey, out);
        log = Log.instance(context);

        if (options == null)
            options = Options.instance(context); // creates a new one

        filenames = new LinkedHashSet<File>();
        classnames = new ListBuffer<String>();
        JavaCompiler comp = null;
        /*
         * TODO: Logic below about what is an acceptable command line
         * should be updated to take annotation processing semantics
         * into account.
         */
        try {
            if (args.length == 0
                    && (classNames == null || classNames.length == 0)
                    && fileObjects.isEmpty()) {
                Option.HELP.process(optionHelper, "-help");
                return Result.CMDERR;
            }

            Collection<File> files;
            try {
                files = processArgs(CommandLine.parse(args), classNames);
                if (files == null) {
                    // null signals an error in options, abort
                    return Result.CMDERR;
                } else if (files.isEmpty() && fileObjects.isEmpty() && classnames.isEmpty()) {
                    // it is allowed to compile nothing if just asking for help or version info
                    if (options.isSet(HELP)
                        || options.isSet(X)
                        || options.isSet(VERSION)
                        || options.isSet(FULLVERSION))
                        return Result.OK;
                    if (JavaCompiler.explicitAnnotationProcessingRequested(options)) {
                        error("err.no.source.files.classes");
                    } else {
                        error("err.no.source.files");
                    }
                    return Result.CMDERR;
                }
            } catch (java.io.FileNotFoundException e) {
                warning("err.file.not.found", e.getMessage());
                return Result.SYSERR;
            }

            boolean forceStdOut = options.isSet("stdout");
            if (forceStdOut) {
                log.flush();
                log.setWriters(new PrintWriter(System.out, true));
            }

            // allow System property in following line as a Mustang legacy
            boolean batchMode = (options.isUnset("nonBatchMode")
                        && System.getProperty("nonBatchMode") == null);
            if (batchMode)
                CacheFSInfo.preRegister(context);

            // FIXME: this code will not be invoked if using JavacTask.parse/analyze/generate
            // invoke any available plugins
            String plugins = options.get(PLUGIN);
            if (plugins != null) {
                JavacProcessingEnvironment pEnv = JavacProcessingEnvironment.instance(context);
                ClassLoader cl = pEnv.getProcessorClassLoader();
                ServiceLoader<Plugin> sl = ServiceLoader.load(Plugin.class, cl);
                Set<List<String>> pluginsToCall = new LinkedHashSet<List<String>>();
                for (String plugin: plugins.split("\\x00")) {
                    pluginsToCall.add(List.from(plugin.split("\\s+")));
                }
                JavacTask task = null;
                for (Plugin plugin : sl) {
                    for (List<String> p : pluginsToCall) {
                        if (plugin.getName().equals(p.head)) {
                            pluginsToCall.remove(p);
                            try {
                                if (task == null)
                                    task = JavacTask.instance(pEnv);
                                plugin.init(task, p.tail.toArray(new String[p.tail.size()]));
                            } catch (Throwable ex) {
                                if (apiMode)
                                    throw new RuntimeException(ex);
                                pluginMessage(ex);
                                return Result.SYSERR;
                            }
                        }
                    }
                }
                for (List<String> p: pluginsToCall) {
                    log.printLines(PrefixKind.JAVAC, "msg.plugin.not.found", p.head);
                }
            }

            comp = JavaCompiler.instance(context);

            // FIXME: this code will not be invoked if using JavacTask.parse/analyze/generate
            String xdoclint = options.get(XDOCLINT);
            String xdoclintCustom = options.get(XDOCLINT_CUSTOM);
            if (xdoclint != null || xdoclintCustom != null) {
                Set<String> doclintOpts = new LinkedHashSet<String>();
                if (xdoclint != null)
                    doclintOpts.add(DocLint.XMSGS_OPTION);
                if (xdoclintCustom != null) {
                    for (String s: xdoclintCustom.split("\\s+")) {
                        if (s.isEmpty())
                            continue;
                        doclintOpts.add(s.replace(XDOCLINT_CUSTOM.text, DocLint.XMSGS_CUSTOM_PREFIX));
                    }
                }
                if (!(doclintOpts.size() == 1
                        && doclintOpts.iterator().next().equals(DocLint.XMSGS_CUSTOM_PREFIX + "none"))) {
                    JavacTask t = BasicJavacTask.instance(context);
                    // standard doclet normally generates H1, H2
                    doclintOpts.add(DocLint.XIMPLICIT_HEADERS + "2");
                    new DocLint().init(t, doclintOpts.toArray(new String[doclintOpts.size()]));
                    comp.keepComments = true;
                }
            }

            fileManager = context.get(JavaFileManager.class);

            if (!files.isEmpty()) {
                // add filenames to fileObjects
                comp = JavaCompiler.instance(context);
                List<JavaFileObject> otherFiles = List.nil();
                JavacFileManager dfm = (JavacFileManager)fileManager;
                for (JavaFileObject fo : dfm.getJavaFileObjectsFromFiles(files))
                    otherFiles = otherFiles.prepend(fo);
                for (JavaFileObject fo : otherFiles)
                    fileObjects = fileObjects.prepend(fo);
            }
            comp.compile(fileObjects,
                         classnames.toList(),
                         processors);

            if (log.expectDiagKeys != null) {
                if (log.expectDiagKeys.isEmpty()) {
                    log.printRawLines("all expected diagnostics found");
                    return Result.OK;
                } else {
                    log.printRawLines("expected diagnostic keys not found: " + log.expectDiagKeys);
                    return Result.ERROR;
                }
            }

            if (comp.errorCount() != 0)
                return Result.ERROR;
        } catch (IOException ex) {
            ioMessage(ex);
            return Result.SYSERR;
        } catch (OutOfMemoryError ex) {
            resourceMessage(ex);
            return Result.SYSERR;
        } catch (StackOverflowError ex) {
            resourceMessage(ex);
            return Result.SYSERR;
        } catch (FatalError ex) {
            feMessage(ex);
            return Result.SYSERR;
        } catch (AnnotationProcessingError ex) {
            if (apiMode)
                throw new RuntimeException(ex.getCause());
            apMessage(ex);
            return Result.SYSERR;
        } catch (ClientCodeException ex) {
            // as specified by javax.tools.JavaCompiler#getTask
            // and javax.tools.JavaCompiler.CompilationTask#call
            throw new RuntimeException(ex.getCause());
        } catch (PropagatedException ex) {
            throw ex.getCause();
        } catch (Throwable ex) {
            // Nasty.  If we've already reported an error, compensate
            // for buggy compiler error recovery by swallowing thrown
            // exceptions.
            if (comp == null || comp.errorCount() == 0 ||
                options == null || options.isSet("dev"))
                bugMessage(ex);
            return Result.ABNORMAL;
        } finally {
            if (comp != null) {
                try {
                    comp.close();
                } catch (ClientCodeException ex) {
                    throw new RuntimeException(ex.getCause());
                }
            }
            filenames = null;
            options = null;
        }
        return Result.OK;
    }

最主要的其实就是那个最长的重载形式,但是发现里面好多从没见过的类,瞬间感觉自己就是个麻瓜。

不知道的太多,效率太低,我决定还是换个思路,待日后再重读javac源码,目前直奔主题为好,我就想知道个基本类型而已。。。

换个思路


既然int这些并没有通过类来保存,那么我猜测是编译的时候通过匹配的方式来了解这是啥基本类型,然后进行分配,那么肯定在某个类里会出现字符串类型的”int”,”short”,”byte”等,而这些东西是Java的开发公司sun公司完成的,又方便javac调用的,那么还是优先在com.sun包内查找。

在com.sun.codemodel.internal包中,有一个JJavaName.java类,这里面包含了java的全部53个关键字:

/** All reserved keywords of Java. */
    private static HashSet<String> reservedKeywords = new HashSet<String>();

    static {
        // see http://java.sun.com/docs/books/tutorial/java/nutsandbolts/_keywords.html
        String[] words = new String[]{
            "abstract",
            "boolean",
            "break",
            "byte",
            "case",
            "catch",
            "char",
            "class",
            "const",
            "continue",
            "default",
            "do",
            "double",
            "else",
            "extends",
            "final",
            "finally",
            "float",
            "for",
            "goto",
            "if",
            "implements",
            "import",
            "instanceof",
            "int",
            "interface",
            "long",
            "native",
            "new",
            "package",
            "private",
            "protected",
            "public",
            "return",
            "short",
            "static",
            "strictfp",
            "super",
            "switch",
            "synchronized",
            "this",
            "throw",
            "throws",
            "transient",
            "try",
            "void",
            "volatile",
            "while",

            // technically these are not reserved words but they cannot be used as identifiers.
            "true",
            "false",
            "null",

            // and I believe assert is also a new keyword
            "assert",

            // and 5.0 keywords
            "enum"
            };
        for (String w : words)
            reservedKeywords.add(w);
    }

除此之外com.sun.codemodel.internal包里的其他类都是规定了许多语法的格式,包括类的种类:

package com.sun.codemodel.internal;
/**
 * This helps enable whether the JDefinedClass is a Class or Interface or
 * AnnotationTypeDeclaration or Enum
 *
 * @author
 *     Bhakti Mehta ([email protected])
 */
public final class ClassType {

    /**
     * The keyword used to declare this type.
     */
    final String declarationToken;

    private ClassType(String token) {
        this.declarationToken = token;
    }

    public static final ClassType CLASS = new ClassType("class");
    public static final ClassType INTERFACE = new ClassType("interface");
    public static final ClassType ANNOTATION_TYPE_DECL = new ClassType("@interface");
    public static final ClassType ENUM = new ClassType("enum");
}

还有比如很熟悉的for循环、while循环,也有以前从没用过的语法的格式。

找到了保存关键字及对其操作的类,我决定看看javac的源码中有哪个类导入了JJavaName类。

结果是在javac的源码中并没有找到有任何一个类调用了JJavaName类。

但是在com.sun.tools.javac中,确实找到了很多蛛丝马迹,表明在编译期的时候,基础数据类型关键字被转换成了相应的指令码。

在com.sun.tools.javac.jvm包下的ByteCoded类中包含了字节码的指令码,以及用作指令修饰符的类型码。

抱着好奇心,写了一段测试程序,想直观地看一下转换结果:

public class BaseTest {

    public static void main(String argc[]) {  
        int c = 3;   
        short b = (short) 4;
        byte a = (byte) 5;
        long d = (long) 2;
        float e = 10.0f;
        double f = 20.0d;
        boolean g = false;
        boolean h = true;
        boolean i = false;
        char j = 'A';
        Integer k = new Integer(88);
        System.out.println(a);
        System.out.println(b);
        System.out.println(c);
        System.out.println(d);
        System.out.println(e);
        System.out.println(f);
        System.out.println(g);
        System.out.println(h);
    }

}
D:\>javap -c BaseTest.class
Compiled from "BaseTest.java"
public class BaseTest {
  public BaseTest();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_3
       1: istore_1
       2: iconst_4
       3: istore_2
       4: iconst_5
       5: istore_3
       6: ldc2_w        #2                  // long 2l
       9: lstore        4
      11: ldc           #4                  // float 10.0f
      13: fstore        6
      15: ldc2_w        #5                  // double 20.0d
      18: dstore        7
      20: iconst_0
      21: istore        9
      23: iconst_1
      24: istore        10
      26: iconst_0
      27: istore        11
      29: bipush        65
      31: istore        12
      33: new           #7                  // class java/lang/Integer
      36: dup
      37: bipush        88
      39: invokespecial #8                  // Method java/lang/Integer."<init>":(I)V
      42: astore        13
      44: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
      47: iload_3
      48: invokevirtual #10                 // Method java/io/PrintStream.println:(I)V
      51: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
      54: iload_2
      55: invokevirtual #10                 // Method java/io/PrintStream.println:(I)V
      58: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
      61: iload_1
      62: invokevirtual #10                 // Method java/io/PrintStream.println:(I)V
      65: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
      68: lload         4
      70: invokevirtual #11                 // Method java/io/PrintStream.println:(J)V
      73: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
      76: fload         6
      78: invokevirtual #12                 // Method java/io/PrintStream.println:(F)V
      81: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
      84: dload         7
      86: invokevirtual #13                 // Method java/io/PrintStream.println:(D)V
      89: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
      92: iload         9
      94: invokevirtual #14                 // Method java/io/PrintStream.println:(Z)V
      97: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
     100: iload         10
     102: invokevirtual #14                 // Method java/io/PrintStream.println:(Z)V
     105: return
}

印证了在编译期的时候,基础数据类型关键字被转换成了相应的指令码。

在这里的另一个发现就是,编译成字节码文件之后,byte、short类型都归为int类型了。

猜你喜欢

转载自blog.csdn.net/ving_suixin/article/details/80032043