`

15个易遗忘的Java问题

    博客分类:
  • Java
阅读更多

1.Java中的基本数据类型以及所占内存大小

(1)整形

byte    1字节

short    2字节

int    4字节

long    8字节

(2)浮点型

float    4字节

double    8字节

(3)字符类型

char    2字节(Unicode-16)

(4)布尔类型

    布尔类型boolean比较特殊,尽管Java虚拟机定义了boolean类型,但虚拟机对boolean类型的支持是有限的,没有为boolean值单独设计JVM指令。操作布尔值的表达式在编译之后,它使用的是JVM的int数据类型,也就是占用4个字节。

JVM也不会直接支持boolean数组,boolean数组在编译之后,它的元素采用byte数据类型,用0表示false,1表示true,也就是boolean数组的元素只占用一个字节。

2.UTF-8与Unicode的关系

    Unicode是一个统一的编码标准,将现有的所有字符进行唯一编码。在第一个Unicode版本中,使用两个字节(16位)来表示一个字符,注意这里的字节并非指的是计算机内存中的存储单元,而是一个数学长度单位而已。然而,一个Unicode字符在内存中存储所占用的长度,就需要一个具体的编码规则来实现,比如UTF-8。因此,Unicode只是一个编码标准,而UTF-8是对这个标准的一个实现,UTF-8规定了一个Unicode字符在内存中占用的空间(英文和中文所占空间是不同的,有兴趣的读者可以查阅相关资料)。

代码点指的是可以用于对字符集进行编码的那些数字,比如在16位的Unicode编码字符集中,字符“A”的编码是U+0041,那么0041就是一个代码点。

代码单元指的是字符所占空间的单元。例如在UTF-32中,一个代码单元为32位,一个字符占用32位,恰好使用一个代码单元,这种方式会耗费大量内存。在UTF-16中,一个代码单元为16位,值 U+0000 至 U+FFFF 编码对应一个字符,每个字符占用一个代码单元,但是,对于超过这个范围的那些增补字符的编码,需要两个这样的单元(即32位)。而在UTF-8中,一个代码单元为8位,UTF-8 使用一至四个字节的序列对编码 Unicode 代码点进行编码,原理同UTF-32和UTF-16。

3.String字符串常量

    在Java语言中,一个String字符串常量对应着一个String对象,并且是不可更改和继承的(因为String类被final关键字修饰)。Java语言这样设计,主要是为了使得字符串常量(注意是字符串常量,字符串变量不符合这里所讲的规则)可以共享,因为JVM将字符串常量放入公共的存储池中,不同的变量可以引用相同的字符串常量。

 

public static void main(String[] args) {
    String a = “hello”;
    String b = “hello”;
    System.out.println(a == b);
}

    以上代码运行结果为:true。这就说明a和b引用的是同一String对象。

4.基本数据类型转换规则

    在双操作数运算中,会根据操作数类型将低级的一个类型转换为高级的一个类型。

1)只要两个操作数中有一个是double类型的,另一个将会被转换成double类型,并且结果也是double类型; 

2)只要两个操作数中有一个是float类型的,另一个将会被转换成float类型,并且结果也是float类型; 

3)只要两个操作数中有一个是long类型的,另一个将会被转换成long类型,并且结果也是long类型; 

4)两个操作数(包括byte、short、int、char)都将会被转换成int类型,并且结果也是int类型。

5.按值调用与按引用调用

    按值调用表示方法接收的是调用者提供的参数值。按引用调用表示方法接收的是调用者提供的是调用者提供的参数地址。Java程序设计语言总是按值调用的。下面是反例代码:

 

public void swap(Person a, Person b) {
    Person temp = a;
    a = b;
    b = tem;
}

    以上代码启动交换a和b所引用的对象,但实际编译执行会发现没有成功交换。这也就证明Java不是按引用调用的,a和b仅代表两个Person对象的值,而不是代表两个对象的引用,在参数传递上与int等基本类型的值没有区别。

6.对象初始化

    在使用构造器初始化对象时,首先运行初始化块,然后运行构造器的主题部分。调用构造器的具体初始化步骤如下:

1)类的所有数据域被初始化为默认值(0、false或null)。

2)按照在类中声明的次序,依次执行所有初始化语句和初始化块。

3)如果构造器第一行调用了第二个构造器,则执行第二个构造器。

4)执行构造器的主体。

    类第一次加载的时候,将会进行静态域的初始化。所有的静态初始化语句以及静态初始化块都将依照定义的顺序进行。

    使用super调用构造器的语句必须是子类构造器的第一条语句。

7.数组

    在Java中,子类数组的引用可以转换成父类数组的引用,而不需要采用强制类型转换。

8.继承

    在覆盖一个方法的时候,子类方法不能低于父类方法的可见性。即父类方法是protected的,子类覆盖的方法只能是protected或public的。

9.final修饰的类

    如果将一个类声明为final的,只有其中的方法自动成为final,而不包括域。

10.equals方法

    Object类的equals方法用于检测一个对象与另一个对象是否相等,即判断两个变量的引用是否相同。如果重新定义了equals方法,就必须重新定义hashCode方法,因为向散列表中添加数据时会根据hashCode和equals方法来确定插入位置。如果x.equals(y)返回true,那么x.hashCode()与y.hashCode()的返回值必须相同。

由于枚举值具有固定的实例,因此直接使用“==”就可以判定两个枚举值是否相同,而不需使用equals方法。

11.Class类

    JVM会为每个加载的类生成一个Class类型的实例,用于跟踪对象所属的类,获取Class类型实例的方法如下:

1)Object类中的getClass()方法将会返回一个Class类型的实例。

2)Class.forName(className)可以返回className指定类的Class实例。

3)MyClass.class可以返回MyClass类的Class实例。

12.局部类

    在方法中声明的类称为局部类(也属于内部类),局部类不仅能够访问包含它们的外部类,还可以访问局部变量。不过,可以被访问的局部变量必须被声明为final。

13.代理

    利用代理可以在运行时创建一个实现了一组给定接口的新类,这种功能只有在编译时无法确定需要实现哪个接口时才有必要使用。我们需要提供一个实现InvocationHandler接口的类来处理调用过程。

 

public class SaneseeProxyDemo{
    public static void main(String[] args) {
            //配置第一个代理aProxyInstance,用于代理Integer类型的a。
            Integer a = 1;
            InvocationHandler aHandler = new SaneseeHandler (a);
            Object aProxyInstance = Proxy.newProxyInstance(null, new Class[]{Comparable.class}, aHandler); //其中第一个参数null表示使用默认的类加载器,             //第二个参数表明需要代理类实现的接口,第三个参数为调用处理器类
            //配置第一个代理bProxyInstance,用于代理Integer类型的b。
            Integer b = 2;
            InvocationHandler bHandler = new SaneseeHandler (b);
            Object bProxyInstance = Proxy.newProxyInstance(null, new Class[]{Comparable.class}, bHandler);
            //输出两个代理类的比较结果
            Comparable aComparable = (Comparable)aProxyInstance;
            Comparable bComparable = (Comparable)bProxyInstance;
            System.out.println(aComparable.compareTo(b));
        }
    }
    class SaneseeHandler implements InvocationHandler
    {
        //需要代理的对象
        private Object target; 
        public SaneseeHandler (Object t)
        {
            target = t;
        }
        public Object invoke(Object proxy, Method m, Object[] args) throws Throwable
        {
            //打印被代理的对象
            System.out.print(target);
            // 打印方法名
            System.out.print("." + m.getName() + "(");
            // 打印参数
            if (args != null)
            {
                for (int i = 0; i < args.length; i++)
                {
                    System.out.print(args[i]);
                    if (i < args.length - 1) System.out.print(", ");
                }
            }
            System.out.println(")");
            // 调用方法
            return m.invoke(target, args);
        }
}

14.限定泛型变量

    关于类型变量限定的问题,一直以来是不少程序员感觉困惑的地方,本条就这个问题简要讨论一下。Java泛型一面有以下两种类型限定的泛型:

<T extends Comparable>

<T super Comparable>

    限定类型可以有多个,使用“&”分隔。比如:

<T super Comparable & Serializable>

    无论何时定义一个泛型类型,都自动提供了一个相应的原始类型。程序运行时,擦除类型变量,并替换为第一个限定类型(比如<T extends Comparable>被替换为Comparable),无限定的变量使用Object。

SaneseeDemo <T>

    不能用类型参数代替基本类型。因此没有SaneseeDemo<int>,只有SaneseeDemo<Integer>。

    不能实例化参数化类型的数组,例如:

SaneseeDemo<Integer>[] array = new SaneseeDemo<Integer>[10]; //这种写法是错误的,不能通过编译。

    这是因为array被擦除类型之后,它的类型为SaneseeDemo [],元素类型为SaneseeDemo,则相应的原始类型为Object[],那么就可以往里面添加任何类型的元素,也就会出现类型错误。

    如果需要使用参数化类型对象,只有一种安全而有效的方法:使用ArrayList。

    同时,也不能在静态域或方法中引用类型变量,无法通过编译,比如:

 

class SaneseeSingleton<T> {
    public static T t;
    public static T getInstance() {
        //Get instance statements;
    }
}

    因为类型擦除之后,就变为原始类型Object了。如果它可以正常执行,那么任何类型的T最终都会变为Object,那么不管传入什么类型的T,最终只能获取相同的单例,这与我们想要的功能是不一致的。

    不能抛出或捕获泛型类型的异常。

    Sanesee<String>不是Sanesee<Object>的子类,因此不能将Sanesee<String>类型的值赋给Sanesee<Object>类型的值。

    不能向<? extends Employee >类型的变量调用set方法的。假设它可以执行,因为程序无法知道这个变量的具体类型,它的类型可能是Manager,也可能是Executive,那么就会出现类型转换错误。只能向这个变量调用get方法,因为程序把获得的值自动转换为Employee类型,子类型可以自动转换为父类型。

    不能向<? super Employee>类型的变量调用get方法,因为程序无法知道返回的具体类型,它的类型可能是Person,也有可能是Object。只能调用set方法,因为不管是传入Employee还是其子类,都可以成功执行。

    总之,带有super限定的通配符可以向泛型对象写入,带有extends限定的通配符可以从泛型对象读取。而Sanesee<?>和Sanesee类型的不同点在于,Sanesee<?>类型擦除以后就是Object了,所以根本无法使用Object类型的对象去调用它,只有将它放在静态方法中执行一些简单的操作。

15.多线程

    当对一个线程调用interupt方法时,线程的中断状态将被置位。每个线程都具有一个boolean类型的中断标志,通过它来判断线程是否被中断:

Thread.currentThread().isInterupted()

    线程的run方法不能抛出任何受检异常,而受检异常会导致程序终止。然而,不需要使用catch语句来处理可以被传播的异常。相反,就在线程死亡之前,异常被传递到一个用于未捕获异常的处理器。可以使用setUncaughtExceptionHandler为任何线程安装一个处理器。

    可以调用ReentrantLock的lock和unlock方法获取锁或解除锁,也可以调用条件对象Condition(由ReentrantLock的newCondition方法获取)的await和singal实现等待或通知功能。两种方式的不同点在于,lock获取到的锁,会等到程序执行结束调用unlock时才会释放,哪怕正在执行耗时比较长的任务,或者处于等待状态。而Condition在需要等待的地方调用await方法,进入等待集,当锁可用时,该线程继续保持阻塞状态,直到另一个线程调用同一条件上的singalAll方法为止(singal方法可以随机解除一个线程的阻塞状态)。然而,最好不使用Lock/Condition或synchronized关键字,可以使用java.util.concurrent包中的任何一种机制,它会为你处理所有的加锁。如果一定得使用关键字,优先使用synchronized,这样可以减少代码的数量,其次才是Lock/Condition。

    ThreadLocal类型可以使得每个线程拥有自己的独立的变量。volatile类型为实例域的访问提供了一种免锁机制,可以使得线程每次都可以访问到最新的值(volatile变量不能保证原子性)。

    ReentrantReadWriteLock类适合读数据多而写数据少的情形:

ReentrantReadWriteLock.readLock();//多个线程共享,排斥写

ReentrantReadWriteLock.writeLock();//单个线程使用,排斥读写

    官方建议放弃使用stop和suspend方法,是因为stop强制终止一个线程是极不安全的操作,而suspend本身容易导致死锁。

    在阻塞队列中,生产者向队列中插入元素,消费者向队列获取元素。当队列为空时,消费者线程会被阻塞;当队列慢时,生产者线程将被阻塞。LinkedBlockingQueue可以不限容量,而ArrayBlockingQueue需要指定容量。PriorityBlockingQueue是一个带有优先级的队列,DelayQueue需要在指定延迟慢之后才能移除元素。LinkedTransferQueue的transfer(item)方法允许生产者线程等待,直到消费者准备就绪并移除这个item元素。

    java.util.concurrent包提供了Map、集合等的并发实现,ConcurrentHashMap、ConcurrentSkipListMap、ConcurrentSkipListSet和ConcurrentLinkedQueue,允许并发访问数据结构。

    任何集合类型可以通过使用同步包装器变成线程安全的(当然最好使用java.util.concurrent包中定义的集合,因为它们经过了精心的设计):

List<E> list = Collections.synchronizedList(new ArrayList<E>);

Map<K, V> map = Collections.synchronizedMap(new HashMap<K, V>());

Runnable封装了一个异步运行的任务,可以看做没有参数和返回值的异步方法。Callable与Runnable类似,但它有返回值。

Executors是一个线程执行器,用于管理线程的创建和执行,使用它可以创建多种线程池,常见线程池如下:

Executors.newCachedThreadPool:必要时创建新线程,空闲线程会保留60秒。

Executors.newFixedThreadPool:创建固定容量的线程池。

Executors.newSingleThreadPool:创建只有一个线程的线程池。

 

Executors.newScheduledThreadPool:用于预定指定的线程池。

2
4
分享到:
评论

相关推荐

    java基础易忘

    ### Java基础易忘知识点详解 #### 1. Java编译过程 - **源代码到字节码**: `.java` 文件经过编译后会生成 `.class` 文件,即字节码文件。 - **编译原理**: Java 编译器将源代码转换为中间表示形式(字节码),这些...

    背单词系统 java

    【标题】"背单词系统 java" 是一个基于Java语言开发的小型应用,旨在帮助用户学习和记忆英语单词。这样的系统通常包含一系列功能,如单词显示、发音、例句展示、复习提醒等,以提高用户的词汇量和记忆力。Java作为一...

    JAVA教学方法改革与实践.pdf

    但当前一般院校的JAVA教学往往存在以下几个问题: (一)认知片面加理论枯燥,易产生厌学心理。JAVA语言本身是一个完整的知识体系,包括前导后续等多门课程,涉及的理论知识面广、内容多,使得学生在学习初期觉觉得...

    error-prone,将常见的Java错误捕获为编译时错误.zip

    易出错(error-prone)是Java开发者的一个得力助手,它是一个开源的静态代码分析工具,专注于在编译阶段就发现并防止常见的编程错误。这个工具由Google开发并维护,其目的是通过在编译期间捕获潜在的问题,来提高代码...

    良葛格java jdk 5.0学习笔记

    《良葛格java jdk 5.0学习笔记》是一份专为初学者设计的Java编程教程,旨在帮助读者深入理解和掌握Java JDK 5.0版本的关键特性与技术。这份笔记包含了丰富的章节内容,覆盖了从基础到进阶的各个层面,通过详细讲解和...

    手机记忆宝.java

    总之,"手机记忆宝.java"是一款全面的英语学习工具,它结合了科学记忆方法、强大的查询功能和准确的发音指导,旨在打造一个便捷、高效的移动学习环境,助力用户提升英语水平。通过深入研究其源代码,开发者和学习者...

    eris:Java随机数生成器

    这是一个自以为是的新面貌java.util.Random API,按照Java编程目前最好的做法。功能和限制兼容插件:除非另有说明,否则所有算法在功能上均与原始参考实现等效并经过测试。 为性能而设计:实现在内部不提供任何线程...

    TaskAlert 任务提醒 1.2.0.6

    总的来说,"TaskAlert 任务提醒"是一个功能强大的任务管理工具,它的更新版本1.2.0.6解决了之前的问题,使得软件更加可靠。通过有效利用这款软件,用户可以提升工作效率,避免因遗忘任务而产生的困扰。同时,提供...

    MyBatis动态条件通用查询

    总的来说,MyBatis动态条件通用查询是一种高效且灵活的数据库查询方式,它结合了Map的数据结构和MyBatis的动态SQL特性,使得在Java应用程序中实现复杂、可变的查询变得简单易行。通过合理的设计和实现,我们可以极大...

    jsp项目jsp项目jsp项目jsp项目jsp项目jsp项目jsp项目

    该项目的目标是开发一个网络通讯录,以克服传统通讯录携带不便、易遗忘等缺点。以下是项目的关键技术点和步骤: 1. **用户注册与登录功能**: 用户需要注册并登录才能使用通讯录。在数据库层面,这涉及到`USERS`表...

    springboot228高校教师电子名片系统.zip

    通过内嵌Tomcat或Jetty服务器,自动配置Spring等特性,SpringBoot使得Java开发者可以快速地创建一个独立的、生产级别的基于Spring的应用。 【描述】"springboot228高校教师电子名片系统" 提示这是一个专为高校教师...

    在线通讯录-寻求帮助

    在线通讯录是一个旨在克服传统通讯录携带不便和易遗忘问题的网络应用。它提供了一系列功能,使得用户可以方便地管理他们的联系人信息。以下是对这个系统各方面的详细说明: 1. **数据库设计**: - 数据类型的选择...

    Android日程管理系统实训报告.doc

    传统的手写记录方式易遗忘重要事件,而日程管理软件能有效地提醒和管理个人的日程,提高生活和工作效率。 2. **项目目标**:本项目旨在构建一个个人日程管理系统,实现日程的编辑、查询、提醒等功能,帮助用户更好...

    One-Time-Biometric:一次基于令牌的生物特征认证

    传统的密码系统虽然广泛使用,但面临诸如易遗忘、易被破解等问题。因此,一种新型的认证方式——“一次性生物特征认证”应运而生。这种技术结合了生物特征的独特性和一次性令牌的安全性,旨在提供更为可靠的身份验证...

    (附源码)app英语学习系统 毕业设计030959

    这是一个基于Java技术栈开发的英语学习系统毕业设计项目,提供了丰富的功能,旨在为用户创造一个高效、便捷的在线学习环境。该系统的核心亮点在于它的多功能性,包括用户登录、学习打卡、课程管理、单词学习、搜索...

    porzeczki:Porzeczki是一个网络应用程序,可帮助您管理借贷

    总之,Porzeczki是一个用Java开发的实用工具,帮助用户解决日常借贷管理问题。通过学习Porzeczki的源代码,开发者不仅可以掌握借贷管理系统的实现方法,还能深化对Java编程和Web应用开发的理解。

    毕业论文jsp1338个人记事本mysql.doc

    这个系统旨在解决传统手工艺记事方式效率低、易出错的问题,提升个人事务管理的便捷性和准确性。 首先,论文的摘要部分阐述了随着计算机技术的普及,人们越来越依赖计算机来处理日常事务。记事本系统的需求源于日常...

    Smartphone-based-authentication

    在标签"Java"的提示下,我们可以推测这个项目可能是使用Java语言开发的。Java是一种广泛应用于移动应用开发的语言,特别是在Android平台上。因此,这个身份验证协议可能涉及到Java的APIs,如Android的KeyStore系统,...

    Notepad8.4.6安装包

    同时,Notepad++内置了语法高亮和自动完成功能,使得代码更易读,减少了因拼写错误或遗忘语法而导致的错误。 在性能方面,Notepad++以其轻量级和快速响应著称。尽管拥有众多高级功能,但它的内存占用相对较低,不会...

    Python与Oracle数据访问技术白皮书.pdf

    Python作为一门流行的编程语言,因其简洁易读的语法和强大的库支持而被广泛应用于数据处理和分析。Oracle数据库则是企业级数据存储和管理的重要工具,其稳定性和高性能深受业界认可。通过Python与Oracle数据库的集成...

Global site tag (gtag.js) - Google Analytics