一. Java 8的包装类

Java中的8种基本数据类型不支持面向对象的变成机制,也不具备对象的特性:没有成员变量,方法可以调用。为此,Java为这8 种基本数据类型分别提供了对应的

包装类(Byte,Short,Integer,Long,Double,Float,Charater,Boolean)。

从jdk 1.5开始,Java提供了自动装箱和自动拆箱的功能。自动装箱就是可以把一个基本类型变量赋给对应的包装类变量。自动拆箱与之相反。

包装类提供了基本类型变量和字符串之间的转换的方法。有两种方式把字符串类型值转换成基本类型的值

a)利用包装类提供的parseXxx(String s)静态方法(除了Character之外的所有包装类都提供了该方法。)

b)利用包装类提供的Xxx(String s)构造器

String类提供了多个重载valueOf()方法,用于将基本类型变量转换成字符串。

两个128自动装箱后,比较它们的大小并不相等,因此Java 7增强了包装类的功能,为所有的包装类提供了一个静态的compare(xxx val1,xxx val2)方法,来比较两个基本类型值得大小。

Java 8再次增强包装类的功能,可以支持无符号运算。

二.处理对象

2.1 打印对象和toString方法

System.out的println()方法和print()方法只能在控制台输出字符串。

toString()方法是Object类里的一个实例方法,它是一个自我描述方法,所有的Java类都是Object类的子类,因此所有的Java类都有toString()方法。

当程序员直接打印一个对象时,系统会输出该对象的“自我描述”信息,以告诉外界该对象的所有状态信息。当我们使用println()和print()方法打印一个对象时,会自动调用Object类的toString()方 法。因此,下面两行代码的效果完全一样:

System.out.println(person);
System.out.println(person.toString());

Object类提供的toString()方法总是返回该对象实现类的“类名+@+hashCode”值,这个值不能真正实现“自我描述”功能,因此如果用户需要实现自我描述功能,就必须重写Object类的toString()方法。

package com.company;

class Apple{
private String color;
private double weight;
public Apple(String color,double weight)
{
this.color = color;
this.weight = weight; } @Override
public String toString() {
return "一个苹果,它的颜色是"+color+",重量是"+weight;
}
} public class ToStringTest {
public static void main(String[] args){
Apple apple = new Apple("红色",5.68);
System.out.println(apple);
} }

2.2 ==和equals方法

== 和 equals 都可以用来测试两个变量是否相等。== 用来判断基本数据类型时,当且仅当变量的数据类型和变量的值都一致时才返回true。== 用来判断引用类型变量时,只有当它们指向同一个对象时才返回true。不可用于比较类型上没有父子关系的两个对象。

可以使用String对象的equals方法判断两个字符串变量的引用字符串的字符序列是否相等,相等就返回true。

下面程序示范了JVM使用常量池管理字符串直接量的情形

package com.company;

public class StringCompareTest {
public static void main(String[] args){
//s1直接引用常量池中的“疯狂Java”
String s1 = "疯狂Java";
String s2 = "疯狂";
String s3 = "Java";
String s4 = "疯狂"+"Java";//s4后面的字符串值在编译时确定下来,直接引用常量池中的“疯狂Java”
String s5 = s2+s3;//s5后面的字符串值在编译时确定下来,不能直接引用常量池中的“疯狂Java”
//JVM会使用常量池来管理“疯狂Java”,在调用构造器创建一个新的String对象,一共产生了两个字符串对象
String s6 = new String("疯狂Java");//s6调用构造器创建一个新的String对象,s6引用堆内存中的String对象 System.out.println(s1 == s4);//输出true
System.out.println(s1 == s5);//输出false
System.out.println(s1 == s6);//输出false
System.out.println(s4 == s6);//输出false
}
}

equals方法是Object类提供的一个实例方法,因此所有引用变量都可调用该方法来判断是否等于引用变量,但使用这个

方法与==运算符没有区别,同样要求两个引用变量指向同一个对象才返回true。如果希望采用自定义的相等标准,则可

采用重写equals方法实现。

package com.company;

class Person
{
private String name;
private String id; public Person(String name, String id) {
this.name = name;
this.id = id;
} @Override
public boolean equals(Object obj)
{
if(this == obj)
return true;
if(null != obj && obj.getClass() == Person.class)
{
Person person = (Person)obj;
if(person.id == this.id)
{
return true;
}
}
return false;
}
}
public class OverrideEqualsRight {
public static void main(String[] args)
{
Person p1 = new Person("孙悟空","234");
Person p2 = new Person("孙行者","234");
Person p3 = new Person("孙悟饭","567");
System.out.println("p1和p2是否相等?"+p1.equals(p2));
System.out.println("p1和p3是否相等?"+p1.equals(p3)); }
}

三.  类成员

3.1 理解类成员

static关键字修饰的成员就是类成员。类成员可以有4种,包括类变量,类方法,静态初始代码块,内部类等。static不等修饰构造器。类成员只属于类不属于实例。

类变量属于整个类,当系统第一次准备使用该类时,系统会为该类变量分配内存空间,类变量开始生效,直到该类被卸载,该类的类变量所占有的内存才会被系统的垃圾回收机制回收。当类初始化完成后,类变量也初始化完成。

类变量可以通过类来访问,也可以通过类的对象来访问。当通过对象来访问类变量时,系统会在底层转换为通过该类访问变量。

注意类成员不能访问实例成员。因为类成员是属于类的,作用域比实例成员的作用域大。完全可能出现类成员初始化完成,但实例成员还不曾初始化的情况。

3.2 单例(Singleton)类

在一些特殊场景下,不允许自由创建该类的对象,而只允许为该类创建一个对象。为了避免其他类自由创建该类的实例,应该把类的构造器使用private修饰。根据良好的封装原则,一旦把类的构造器隐藏起来,则需要提供一个public方法作为该类的访问点,用于创建该类的对象,且该方法必须使用static修饰(因为调用该方法之前还不存在对象,因此调用该方法的不可能是对象,只能是类)。除此之外,该类还必须缓存已经创建的对象,否则该类无法知道是否曾经创建过对象。为此该类需要使用一个成员变量来保存曾经创建的对象,因为该成员变量需要上面的静态方法访问,故该成员变量必须使用static修饰。

如果一个类始终只能创建一个实例,则这个类被称为单例类。

基于上面介绍,下面程序创建了一个实例类。

package com.company;
class Singleton
{
private static Singleton instance;
private Singleton(){}
public static Singleton getSingleton()
{
if(null == instance)
{
instance = new Singleton();
} return instance;
} } public class SingletonTest {
public static void main(String[] args)
{
Singleton s1 = Singleton.getSingleton();
Singleton s2 = Singleton.getSingleton();
System.out.println(s1 == s2);
} }

输出:true

 四. final修饰符

final关键字可用于修饰类、变量和方法。用于表示它修饰的类,方法和变量不可改变。

4.1 final成员变量

final修饰的成员变量必须由程序员显式地指定初始值,归纳如下:

类变量:必须在声明类变量时或静态初始块中指定初始值。而且只能在两个地方的其中之一指定。

实例变量:必须在声明实例变量时,非静态初始化块或构造器中指定初始值,而且只能在三个地方的其中之一指定。

如果打算在构造器、初始化块中对final成员变量进行初始化,则不要在初始化之前就访问成员变量的值。例如下面程序

引发错误。

public class FinalErrorTest {
final int age;
{
System.out.println(age);//访问引发错误
age = 6;
System.out.println(age);
}
}

4.2 final局部变量

局部变量必须由程序员显示指定默认值。因此使用final修饰局部变量时,既可以在定义时指定默认值,也可以不指定

默认值。例如:

public void setDemo()
{
final int a;
a=3;
System.out.println(a);
}

final修饰基本数据类型变量时,保证变量的基本数值不会改变,当final修饰引用类型变量时,final只保证变量引用的

地址不会改变,即一直引用一个对象。

4.3 可执行宏替换的final变量

对于一个final变量来说,不论是类变量,实例变量,还是局部变量,只要满足三个条件,这个final变量不再是一个变量,而是相当于一个直接量。

a)使用final修饰符修饰

b)在定义该final变量时指定了初始值

c)该初始值在编译时确定了下来

4.4 final方法

final修饰的方法不可以被重写

4.5 final类

final修饰的类不可以有子类,不可被继承,例如java.lang.math就是一个final类

4.6 不可变类

不可变类的意思是创建该类的实例后,该实例的实例变量是不可改变的。Java提供的8个包装类和java.lang.String类

都是不可变类,当创建它们的实例后,其实例的实例变量不可改变

创建自定义的不可变类,需满足以下规则:

1.使用private和final修饰符来修饰该类的成员变量

2.提供带参数的构造器,用于根据传入参数初始化类里的成员变量

3.仅为类的成员变量提供getter方法,不提供setter方法

4.如果有必要,重写Object类的equals()方法和hashCode()方法。equals()方法根据关键成员变量来作为两个对象是否相    等的标准,除此之外,还应该保证两个用equals()方法判断为相等的对象的hashCode()也相等。

下面定义一个不可变的Address类。

package com.company;

public class Address {
private final String detail;
private final String postCode; public Address()
{
this.postCode = "";
this.detail = "";
} public Address(String detail,String postCode)
{
this.detail = detail;
this.postCode = postCode;
} public String getDetail()
{
return this.detail;
} public String getPostCode()
{
return this.postCode;
} @Override
public int hashCode() {
return detail.hashCode() + postCode.hashCode()*31;
} @Override
public boolean equals(Object obj) {
if(this == obj)
return true;
if(null != obj && obj.getClass() == Address.class)
{
Address ad = (Address)obj;
if(this.getDetail().equals(ad.getDetail())
&& this.getPostCode().equals(ad.getPostCode()))
return true;
}
return false;
} }

如果需要设计一个不可变类,尤其要注意其引用类型的成员变量。如果引用类型的成员变量的类是可变的,就必须采取必要的措施来保护该成员变量所引用的对象不会被修改,这样才能创建真正的不可变类。

下面程序试图创建一个不可变的Person类,但因为Person类的一个成员变量是可变类,所以导致Person类也是个可变类。

package com.company2;

class Name
{
private String firstName;
private String lastName; public Name(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public Name(){} public String getFirstName() {
return firstName;
} public String getLastName() {
return lastName;
} public void setFirstName(String firstName) {
this.firstName = firstName;
} public void setLastName(String lastName) {
this.lastName = lastName;
} }
public class Person { private final Name name; public Person(Name name)
{
this.name = name;
} public Name getName() {
return name;
} public static void main(String[] args)
{
Name name = new Name("悟空","孙");
Person person = new Person(name); System.out.println(person.getName().getFirstName()); name.setFirstName("八戒"); System.out.println(person.getName().getFirstName()); }
}

为了保持Person对象的不可变性,必须保护好Person对象的成员变量:name,让程序无法访问到Person对象的name

成员变量,也无法利用name成员变量的可变性来改变Person对象了。

因此将Person类改为如下:

public class Person {

    private final Name name;
public Person(Name name)
{
this.name = new Name(name.getFirstName(),name.getLastName());
} public Name getName() {
return name;
}

4.7 缓存实例的不可变类

不可变类的实例状态不可改变,可以很方便的被多个对象所共享。如果程序经常需要使用相同的不可变类的实例,则

应该考虑缓存这种不可变类的实例,毕竟创建重复的对象没有太大的意义,而且加大系统开销。如果可能,应该将不可变类的实例进行缓存。

package com.company2;

class CacheImmutale
{
private static int MAX_SIZE = 10;
//使用数组来缓存已有的实例
private static CacheImmutale[] cache = new CacheImmutale[MAX_SIZE];
//记录缓存实例在缓存中的位置,cache[pos-1]是最新的实例
private static int pos = 0;
private final String name; private CacheImmutale(String name)
{
this.name = name;
} public String getName() {
return name;
} /**
* 缓存的操作方法
* @param name
* @return 缓存的实例
*/
public static CacheImmutale valueOf(String name){
//遍历已缓存的对象
for(int i = 0 ; i < MAX_SIZE ; i++)
{
//如果已有相同实例,则直接返回该缓存的实例
if(null != cache[i] && cache[i].getName().equals(name))
{
return cache[i];
} }
//如果缓存池已满,
if(pos == MAX_SIZE)
{
//把缓存的第一个对象覆盖,即把刚刚生成的对象放在缓存池的最开始位置
cache[0] = new CacheImmutale(name);
pos = 1;
}
else
{
//把新的对象缓存起来,pos加1
cache[pos++] = new CacheImmutale(name);
}
return cache[pos-1];
} @Override
public boolean equals(Object obj) {
if(this == obj)
return true;
if(null != obj && obj.getClass() == CacheImmutale.class)
{
CacheImmutale cache = (CacheImmutale) obj;
return cache.getName().equals(name);
}
return false;
}
public int hashCode()
{
return name.hashCode();
}
}
public class CachelmmutaleTest {
public static void main(String[] args)
{
CacheImmutale c1 = CacheImmutale.valueOf("Hello");
CacheImmutale c2 = CacheImmutale.valueOf("Hello"); System.out.println(c1 == c2);
}
}

Java面向对象进阶篇(包装类,不可变类)的更多相关文章

  1. Java面向对象进阶篇(抽象类和接口)

    一.抽象类 在某些情况下,父类知道其子类应该包含哪些方法,但是无法确定这些子类如何实现这些方法.这种有方法签名但是没有具体实现细节的方法就是抽象方法.有抽象方法的类只能被定义成抽象类,抽象方法和抽象类 ...

  2. Java面向对象进阶篇(内部类)

    一. 概念 大部分时候,类被定义成一个独立的程序单元.有时候把一个类放在另一个类内部定义,这个类被称为内部类,包含内部类的类也被称为外部类. 内部类的主要作用: 内部类提供良好的封装,可以把内部类隐藏 ...

  3. Python开发【第七篇】:面向对象 和 python面向对象进阶篇(下)

    Python开发[第七篇]:面向对象   详见:<Python之路[第五篇]:面向对象及相关> python 面向对象(进阶篇)   上一篇<Python 面向对象(初级篇)> ...

  4. Java面向对象(概述,构造函数,类与对象的关系,this关键字,成员、局部),匿名对象的调用,构造代码块(5)

    Java面向对象(概述,构造函数,类与对象的关系,this关键字,成员.局部),匿名对象的帝爱用,构造代码块(5)

  5. java中String、包装类、枚举类的引用传递

    一般情况下,我们认为Java中了除了八种基本数据类型,其他都是对象,进行引用传递: 但是:String.包装类.枚举类作为参数传递后发现,没有达到引用传递的效果,很多人认为它是值传递! 首先,对象肯定 ...

  6. Java基本数据类型、包装类与String类之间的转换

    一.基本数据类型与包装类之间的转换: import org.junit.Test; public class MainTest { /** * 基本数据类型与包装类之间的转换 */ @Test pub ...

  7. Java.lang 包 (包装类、String类、Math类、Class类、Object类)

    Java 的核心 API(Application Programming Interface)是非常庞大的,这给开发者带来了很大的方便. java.lang 包是 Java 的核心类库,它包含了运行 ...

  8. ☕Java 面向对象进阶内容

    目录 == 和 equals 方法 封装 多态 抽象类和抽象方法 抽象方法 抽象类 抽象类的使用要点 接口 接口使用 内部类 String 字符串常量拼接时的优化 String Pool String ...

  9. 3.1常用类(java学习笔记)包装类及日期类

    一.包装类 java是一门面向对象的语言,秉承一切皆对象的思想. 可java中有一些基本数据类型并不是对象,有时可能需要将它们变为对象. 这时就需要用到我们的包装类了. 基本数据类型 包装类 int ...

随机推荐

  1. Android 6.0 运行时权限处理问题

    序 自从升级到Android M以来,最大的改变就是增加了运行时权限RuntimePermission,6.0以上的系统如果没有做适配,运行了targetSDK=23的App时就会报权限错误.我们知道 ...

  2. 谈谈Ext JS的组件——布局的使用方法续一

    盒子布局 盒子布局主要作用是以水平(Ext.layout.container.HBox)或垂直方式(Ext.layout.container.VBox)来划分容器区域.这也是比较常有的布局方式. 使用 ...

  3. 【IOS 开发】Object - C 语法 之 类型转换

    作者 : 万境绝尘 (octopus_truth@163.com) 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/3913507 ...

  4. Leetcode_119_Pascal's Triangle II

    本文是在学习中的总结,欢迎转载但请注明出处:http://blog.csdn.net/pistolove/article/details/41851069 Given an index k, retu ...

  5. 7、Libgdx网络操作

    (官网:www.libgdx.cn) Libgdx包含了一些跨平台的网络操作类,这些类在Gdx.net中. 特性 跨平台HTTP请求 多平台TCP C/S Socket支持(可配置) TCP C/S优 ...

  6. 4.1、Libgdx的生命周期

    (原文:http://www.libgdx.cn/topic/32/4-1-libgdx%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F) 一个libgdx应 ...

  7. Swift语言学习路线图

  8. (二十五)键盘的设置与TextField细节处理

    设置Return Key类型为Send并且勾选下面的复选框即可实现没有内容时禁用keyboard,有内容自动启用. 文本框左边框与文本留有间距的方法: //文本框左侧留下间距 UIView *left ...

  9. AngularJS进阶(二十三)ANGULAR三宗罪之版本陷阱

    ANGULAR三宗罪之版本陷阱 坑!碰到个大坑,前面由于绑定日期时将angular版本换为angular-1.3.0-beta.1时,后来午睡后,登录系统,发现无论如何都登陆不进去了,经过调试,发现数 ...

  10. SpriteBuilder中子节点的相对位置(%百分比定位)

    子节点(或在这里确切的为精灵sprites)50%的偏移效果使得其在父节点中居中显示,该父节点的纹理在左下角(锚点为0,0). 这样做好过用父节点的位置的实际值来定位.根据父节点实际位置来定位在早期的 ...