资讯专栏INFORMATION COLUMN

java中的一些惯用法总结

zhangke3016 / 989人阅读

摘要:字符串字符串是存储在字符串常量池中的。面试题假设字符串常量池中不存在字符串,那么创建了几个对象答两个堆空间的值字符数组和字符串常量池中的实体。程序运行期间,静态存储的数据将随时等候调用。中的数组会自动记性范围检查会造成少量的内存开销。

字符串

字符串是存储在字符串常量池中的。例如以下的两个字符串的内存地址值是一样的:

String str1 = "hello" + "world";
String str2 = "helloworld";
System.out.println(str1 == str2);      // true
System.out.println(str1.equals(str2)); // true

String str3 = "hello";
String str4 = "world";
String str5 = str3 + str4;
System.out.println(str5 == str2);      // false
System.out.println(str5.equals(str2)); // true

在以上的代码中str2和str5的地址值不相同,如果我们对str5使用intern()方法即:

String str6 = str5.intern();           // native方法
System.out.println(str6 == str2);      // true

就可以返回true。

面试题:假设字符串常量池中不存在字符串"hello",那么String s = new String("hello")创建了几个对象?

:两个堆空间的value值(字符数组)和字符串常量池中的hello实体。我们可以通过查看new String(String str)的源码:

 /** The value is used for character storage. */
 private final char value[];

 /**
 * Initializes a newly created {@code String} object so that it represents
 * the same sequence of characters as the argument; in other words, the
 * newly created string is a copy of the argument string. Unless an
 * explicit copy of {@code original} is needed, use of this constructor is
 * unnecessary since Strings are immutable.
 *
 * @param  original
 *         A {@code String}
 */
public String(String original) {
    this.value = original.value;
    this.hash = original.hash;
}
用句柄操作对象

java中一切皆对象,但是我们操作的实际上是指向这个对象的句柄(Handle),这个句柄也叫做引用(Reference)或者指针(Pointer)。我们可以将这一情形想象成用遥控器(Handler)操作电视机(Object)。没有电视机,遥控器。

即使没有电视机,遥控器也可以多带带存在。即句柄是可以多带带存在的(并不指向任何实体)。例如:

String s;

以上的java代码创建的仅仅是句柄而不是对象。此时如果向s发送一条消息,将会或者一个运行时异常,因此更安全的做法是:创建一个句柄的时候进行显式初始化:

String s = "";
java程序运行时数据的存放位置

Register。处理器内部(最快),由于数量有限,所以寄存器是根据需要有编译器分配的。我们对它没有直接的控制权,也不可能在程序中找到寄存器的任何踪迹。

Stack。驻留与常规RAM区域,速度仅次于寄存器。我们可以通过它的“堆栈指针”获得直接的处理支持。(指针下移创建新的内存;指针上移释放内存)。创建程序时java编译器必须准确知道堆栈内保存的所有数据的“长度”以及“存在的时间”(必须生成相应的代码,以便于向上或者向下移动指针)。这一限制无疑影响了程序的灵活性,所以java将对象的句柄保存在堆栈中,但是对象并不存放在其中。

Heap。一种常规用途的内存池(也是在RAM区域),保存java对象。Heap最吸引人的地方在于:编译器并不必要知道要从堆中分配多少存储空间,也不必要知道存储的数据要在堆中停留多长时间——用堆保存数据会得到更大的灵活性。然而every coin have two slices,在堆中分配存储空间会花费更长的时间!

静态存储。“静态”(Static)指的是“位于固定位置”。程序运行期间,静态存储的数据将随时等候调用。我们可以用static关键字指出一个对象的特定元素是静态的,但是java对象本身永远不会置入静态存储空间

常数存储。常数存储通常直接置于一个程序代码内部。这样做是安全的,因为他们永远不会被改变。有的常数需要严格保护,可以考虑将他们置入只读存储器(ROM)。

非RAM存储:将数据完全保存子啊一个程序之外。对典型的2个例子就是“流式对象”和“固定对象”。

流式对象:对象-->字节流-->另一台机器

固定对象:对象-->磁盘

基本数据类型

基本数据类型由于比较常用,而堆栈的效率又高于堆。所以基本数据类型都是保存在堆栈中。对于基本数据类型我们不需要用new,而是创建了一个并非句柄的“自动变量”,该变量容纳了具体的值,并保存在堆中可以更高效的存取。

java中的数组

在C、C++中使用数组是非常危险的,因为那些数组只是内存块,如果程序访问自己内存块之外的数据或者在初始化之前使用内存会产生不可预料的后果。在C++中应该尽量避免使用数组而换用Standard TemplateLibrary中更安全的容器。java中的数组会自动记性范围检查会造成少量的内存开销。但是我们可以换来更高的工作效率。

变量作用域

变量的作用域是由花括号的位置决定的。
在C、C++中以下的代码是合法的:

{
    int x = 10;
    {
        int x = 100; // 不合法Duplicate local variable x
    }
}

但是在java中编译器会认为变量x已经被定义,所以C、C++能将一个变量“隐藏”在一个更大的作用域中,java的设计者认为这样使程序产生了混淆。

注意区分成员变量和局部变量

成员变量都有默认值,而局部变量必须进行初始化。

文档注释

文档注释只能为public和protected的成员处理文档,private和default的不会被javadoc提取。文档注释中可以嵌入html,例如:

异常

方法抛出异常的时候,该方法会从栈上立即被取出,而异常再度丢给栈上的方法(也就是调用方),如果调用方没有对异常进行处理而是继续抛出异常,调用方就会从栈上弹出,异常再度交给此时的栈顶方法,如此一路下去……
finally中的代码有一种情况下是执行不到的:finally的前面出现了System.exit(0)。

static和final

static只能修饰类的成员(变量和方法),不能修饰局部变量。static变量存放在方法区。随着类的加载而加载,存在方法区中,随着类的消失而消失,生命周期最长。如果没有给定初值,static变量会被默认置0.(或者null)。

Q:如果一个类被标记为final,再将该类中的方法标记位final是不是很多余?
A:不只是多余,而且是多了很多!如果一个类为final,那么它根本就没有子类,根本不可能覆写父类中的方法(只有继承才有覆写)。

使用类加载器加载配置文件

工程目录如下:

加载该配置文件应该这样写:

public class Test {

    @org.junit.Test
    public void test() throws IOException {
        
        ClassLoader classLoader = this.getClass().getClassLoader();
        InputStream is = classLoader.getResourceAsStream("org/gpf/conf/db.properties");
        Properties properties = new Properties();
        properties.load(is);
        properties.list(System.out);
    }
}
利用反射获取父类的泛型
public class Person {
    // some code...
}

public class Student extends Person {
    // some code...
}

Class clazz =  Student.class;
Type type = clazz.getGenericSuperclass(); // org.gpf.Person
ParameterizedType parameterizedType = (ParameterizedType) type; // type的子接口
for (Type c : parameterizedType.getActualTypeArguments()) {
    System.out.println(((Class)c).getName()); // Class是Type接口的实现类
}
java中的枚举

所谓枚举,就是枚举类的对象的个数是有限的,可以穷举出来的类。实际上单例模式也可以使用枚举来实现(Effective Java中的单例经典实现,枚举只有一个成员)。jdk1.5之前需要自定义枚举类,jdk1.5之后提供了enum关键字用于定义枚举类。

例如季节是有限的:春夏秋冬。

// jdk1.5之前的枚举类
public class Season {

    // 1.声明final属性
    
    private final String seasonName;     // 季节名
    private final String seasonDescribe; // 季节描述
    
    //  2.为保证实例的数目是确定的需要私有化构造器,在构造器中初始化final的属性
    private Season(String seasonName,String seasonDescribe) {
        this.seasonName = seasonName;
        this.seasonDescribe = seasonDescribe;
    }

    // 3.通过公用的方法调用属性
    public String getSeasonName() {
        return seasonName;
    }

    public String getSeasonDescribe() {
        return seasonDescribe;
    }
    
    // 4.内部实例化枚举类的对象
    public static final Season SPRING = new Season("spring", " 春暖花开");
    public static final Season SUMMER = new Season("summer", " 夏日炎炎");
    public static final Season FALL = new Season("fall", " 秋高气爽");
    public static final Season WINTER = new Season("spring", " 白雪皑皑");

    public String show() {
        return "Season [seasonName=" + seasonName + ", seasonDescribe="
                + seasonDescribe + "]";
    }
    
}

我们可以使用以下的方式进行调用

Season season = Season.FALL;
System.out.println(season.show());
System.out.println(season.getSeasonName() + "-->" + season.getSeasonDescribe());

jdk1.5之后我们可以使用enum关键字来简化枚举类的定义:

// jdk1.5之后的枚举类
public enum Season {

    SPRING("spring", " 春暖花开"),
    SUMMER("summer", " 夏日炎炎"),
    FALL("fall", " 秋高气爽"),
    WINTER("spring", " 白雪皑皑");
    
    private final String seasonName;     // 季节名
    private final String seasonDescribe; // 季节描述
    
    private Season(String seasonName,String seasonDescribe) {
        this.seasonName = seasonName;
        this.seasonDescribe = seasonDescribe;
    }

    public String getSeasonName() {
        return seasonName;
    }

    public String getSeasonDescribe() {
        return seasonDescribe;
    }
    
    public String show() {
        return "Season [seasonName=" + seasonName + ", seasonDescribe="
                + seasonDescribe + "]";
    }
    
}

这样使用枚举类:

Season season = Season.FALL;
System.out.println(season.show());
System.out.println(season.getSeasonName() + "-->" + season.getSeasonDescribe());

Season[] seasons = Season.values(); // 返回所有枚举类的对象的数组
for (Season s : seasons) {
    System.out.println(s.getSeasonName());
}

season = Season.valueOf("SUMMER"); // 返回枚举类型的对象
System.out.println(season);

我们也可以让枚举类型实现接口:

// jdk1.5之后的枚举类
public enum Season implements Info{

    SPRING("spring", " 春暖花开"){
        @Override
        public void show() {
            System.out.println(1);
        }
    },
    SUMMER("summer", " 夏日炎炎"){
        @Override
        public void show() {
            System.out.println(2);
        }
    },
    FALL("fall", " 秋高气爽"){
        @Override
        public void show() {
            System.out.println(3);
        }
    },
    WINTER("spring", " 白雪皑皑"){
        @Override
        public void show() {
            System.out.println(4);
        }
    };
    
    private final String seasonName;     // 季节名
    private final String seasonDescribe; // 季节描述
    
    private Season(String seasonName,String seasonDescribe) {
        this.seasonName = seasonName;
        this.seasonDescribe = seasonDescribe;
    }

    public String getSeasonName() {
        return seasonName;
    }

    public String getSeasonDescribe() {
        return seasonDescribe;
    }
    
    @Override
    public void show() {
        System.out.println("Season [seasonName=" + seasonName + ", seasonDescribe="
                + seasonDescribe + "]");
    }
    
}

以上的程序中每个枚举类型的实例都各自实现自己的方法!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/64732.html

相关文章

  • Java 持有对象(11)

    摘要:如果一个程序只包含固定数量且其生命周期都是已知的对象,那么这是一个非常简单的程序。 如果一个程序只包含固定数量且其生命周期都是已知的对象,那么这是一个非常简单的程序。 1.泛型和类型安全的容器 通过使用泛型,可以在编译期防止将错误类型的对象放置到容器中. 2.基本概念 Java容器类库的用途是保存对象,并将其划分为两个不同的概念:Collection,Map. Collection...

    summerpxy 评论0 收藏0
  • Java编程思想》笔记11.持有对象

    摘要:迭代器通常被成为轻量级对象创建它的代价很小。与迭代器可以用于数组和所有对象,之所以能够工作,是因为继承了接口。 点击进入我的博客 我觉得本章名字改成容器似乎更好理解,持有对象让人感到一头雾水我们需要在任意时刻和任意位置创建任意数量的对象,所以依靠创建命名的引用来持有对象已经满足不了需求。Java可以用数组和其他容器类来(List、Set、Queue、Map)来解决这个问题,不同的容器...

    newtrek 评论0 收藏0
  • js中的立即执行函数

    摘要:匿名函数的好处在于可以减少局部变量,以免污染现有的运行环境。另外通过,这三个符号运行的匿名函数比运行的匿名函数可以减少一个字符的使用但是我们通常使用加因为其他的操作符可能会带来其他的影响更多可以参考 js中的立即执行函数 ( function(){…} )()和( function (){…} () )是两种javascript立即执行函数的常见写法 问题: 为什么会出现上面的两种不一...

    zhunjiee 评论0 收藏0
  • Javascript知识点:IIFE - 立即调用函数

    摘要:所以那些匿名函数附近使用括号或一些一元运算符的惯用法,就是来引导解析器,指明运算符附近是一个表达式。 Immediately-invoked Function Expression(IIFE,立即调用函数),简单的理解就是定义完成函数之后立即执行。因此有时候也会被称为自执行的匿名函数(self-executing anonymous function)。 IIFE的叫法最早见于Ben...

    goji 评论0 收藏0

发表评论

0条评论

zhangke3016

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<