第十一天

一、什么是包装类

 

包装类(Wrapper Class): Java是一个面向对象的编程语言,但是Java中的八种基本数据类型却是不面向对象的,为了使用方便和解决这个不足,在设计类时为每个基本数据类型设计了一个对应的类进行代表,这样八种基本数据类型对应的类统称为包装类(Wrapper Class),包装类均位于java.lang包。

 

二、包装类的用途

1、为了使用方便Java中将8种基本数据类型进行了封装:除了Integer和Character类以外,其它六个类的类名和基本数据类型一直,只是类名的第一个字母大写即可。

boolean —> Boolean

char —> Character

byte—> Byte

short—> Short

long—> Long

int —> Integer

float—> Float

double—> Double

对于包装类说,用途主要包含两种:

a、作为和基本数据类型对应的类型存在,方便涉及到对象的操作。

b、包含两种基本数据类型的相关属性如最大值、最小值等,以及相关的操作方法

三、包装类的实际应用int和integer为例)

1、int和integer类之间的转换

在实际转换时,使用Integer类的构造方法和Integer类内部的intValue方法实现这些类型之间的相互转换:

 

public class WrapTest {
   public static void main(String[] args) {
    int i = 12;
    Integer i1 = new Integer(i);//基本数据类型转的对应的包装类型,用包装的
    
    Integer i2 = new Integer(10);// 创建一个包装类型的整数 10
    int i3 = i2.intValue();//反过来把包装类型变量转对应的基本数据类型,用xxxValue()方法
    }
}

 

2、Interger类内部的常用方法

Integer类的主要方法有parseInt方法和toString方法

public class WrapTest {
   public static void main(String[] args) {
    int i = 12;
    Integer i1 = new Integer(i);//基本数据类型转的对应的包装类型,用包装的
    
    Integer i2 = new Integer(10);// 创建一个包装类型的整数 10
    int i3 = i2.intValue();//反过来把包装类型变量转对应的基本数据类型,用xxxValue()方法
    
    //i是基本数据类型,就是一个字值,他是没有方法和属性(只有类才有的特征)    
    String string ="123";
    Integer i4 = Integer.parseInt(string);//包装类型有方法可以调用,字符串转整型
                                          //基本数据类型没有方法
    Integer i5 = new Integer(111);
    String string2 = Integer.toString(i5);//整型转字符串
    
   }
}

3、自动拆装箱

JDK自从1.5版本以后,就引入了自动拆装箱的语法,也就是在进行基本数据类型和对应的包装类转换时,系统将自动进行,这将大大方便程序员的代码书写。

自动装箱:将 基本数据类型 封装为对象类型,来符合java的面向对象的思想。

自动拆箱:将对象重新转化为基本数据类型。

//5是基本数据类型,通过自动装箱变成对象类型。

Integer iii=5; //编译器执行了Integer iii = Integer.valueOf(5)

int iii2=iii; //自动拆箱,实际上执行了 int iii2 = iii.intValue()

System.out.println(iii2);

 

valueOf()源码分析:

public static Integer valueOf(int i) {

assert IntegerCache.high>= 127;

if (i >= IntegerCache.low&& i <= IntegerCache.high)

return IntegerCache.cache[i+ (-IntegerCache.low)];

return new Integer(i);

}

说明:Integer iii=5;相当于编译器执行了Integer iii = Integer.valueOf(5)操作。这个方法就是返回一个 Integer 对象,只是在返回之前,看作了一个判断,判断当前 i 的值是否在 [-128,127] 区间,且 IntegerCache 中是否存在此对象,如果存在,则直接返回引用,否则,调用构造器执行类型转换!

4.包装类的缓存值

各个包装类缓存值范围 :

boolean:true和false

byte:-128~127

char:0~127

short:-128~127

int:-128~127

long:-128~127

特别注意:对于float和double没有缓存。

 

注意点:在对上述值进行装箱的时候,并不是创建一个新对象而是使用缓存中的对象,如果超出范围才需要新建立对象。

 

三、转成包装类有什么好处呢?

1、首先包装类是一种类,类就有定义好的属性或方法,这些方法对应就完成了一些特定的功能,我们可以调用这些方法完成一些工作,简化了我们自己的编码量,没有这些方法,我们需要这些功能时就要自己编程去实现。

2、基本数据类型都用隐式的默认值,不是类的对象,不能为null,很多时候我们可能需要我们的这些类型可以为null,这时就需要用包装类了!

public class WrapTest {
   public static void main(String[] args) {
    Integer ii = 100;
    Integer iii = 100;
    System.out.println(ii == iii);
   }
} // 结果为true
public class WrapTest {
   public static void main(String[] args) {
    Integer ii = 150;
    Integer iii = 150;
    System.out.println(ii == iii);
   }
} // 结果为false

toString方法、  ==和equals方法

我们通常在控制台输出时使用System.out.print(),print只能直接输出基本类型和字符串,对于其他的类型直接输出将会输出@开头的引用,因此若需要输出对应的内容则需要使用toSring方法。

toString方法属于ObjectObject是所有类的顶级父类,所以所有类都继承了toString方法,在实际使用的时候需要重写toString方法然后实现内容的直接输出。

public class Person {
    private String name;
    private int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override//toString方法在默认继承的父类里边,所以根据自己的需要,需要重写toString
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }
}
public class TestMain {
        public static void main(String[] args){
        Person person = new Person();
        person.setName("张三");
        person.setAge(21);
        System.out.println(person);//默认等同于下面这种写法
        System.out.println(person.toString());
        }
}//输出结果:coursetest.Person@2a139a55
//不重写toString方法结果为coursetest.Person@2a139a55  coursetest.Person@2a139a55
//重写toString方法后结果为Person [name=张三, age=21]   Person [name=张三, age=21]

==用来判断两个变量是否相等,如果是基本类型,则只要值相等,判断的结果就是true,例如65‘A’比较,结果就是true

public class TestMain {
        public static void main(String[] args){
       int  a = 65;
       int  b = 'A';//A对应的ascii码:65
       System.out.println(a == b);
        }
}//结果为true

如果是比较的引用类型,那么只有在两个变量指向的是同一对象时才是true,例如String a="111"String a1=new String("111");他们==比较的结果就是false,因为他们是两个对象。

public class TestMain {
        public static void main(String[] args){
       Person p1 = new Person();
       Person p2 = p1;
       Person p3 = new Person();
       System.out.println(p1==p2);//true
       System.out.println(p1==p3);//false
        }
}

如果想要让两个只要内容相同的对象比较结果就是true,那么可以使用equals来比较,但是需要对equals进行重写。

equals默认情况下内部就是==比较,所以如果不重写的话,比较结果和==比较一样,注意String中已经重写了equals方法,所以我们比较字符串的内容是否相等一般都是用equals方法。

package coursetest;
public class TestMain {
        public static void main(String[] args){
     //字符串保持在内存中的常量池的地方
            String  s1 = "111";
            String  s2 = "111";
               String  s3 = new String("111");//new 关键字new出来的对象都会放到堆内存中
               System.out.println(s1==s2);//true
               System.out.println(s1==s3);//false
               System.out.println(s1.equals(s3));//true  s1和s3的字符串内容是否一样
        } //如果我们要对比的是字符串的内容是不是相同,不能用== ,要用equals
}         //结论以后压迫对比两个应用变量,是不是指向的同一个对象,用==

其他的引用类型的变量比较就要重写equals方法来自定义相等的规则,所以不仅仅是能实现上述的情况,还可以让不相等的对象相等,是看怎么写了。

正确重写equals应遵循的规则:

   1.对于任意xx.equals(x)结果要是true自反性

   2.对于任意xy,如果x.equals(y)true,那么y.equals(x)也应该是true对称性

   3.对于任意的xyz,如果x.equals(y)truey.equals(z)true,那么x.equals(z)也应该是true传递性

   4.对于任意xy,若对象中用于等价比较的信息未变,那么无论调用多少次x.equals(y)的结果应该是同样的,要么都是true要么都是false一致性

   5.对于任何非null的对象,x.equals(null)一定是false

   重写equals方法的时候,一般要用到instanceof和强制类型转换,然后当类型匹配时再进行比较。

一个经典的重写equals的例子:

public class Person {
    private int id;
    private String name;
    private int age;
    
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override//toString方法在默认继承的父类里边,所以根据自己的需要,需要重写toString
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }
    @Override
    public boolean equals(Object obj) {
        if(this == obj) {
            return true;
        }
        if(obj == null) {
            return false;
        }
        if(!(obj instanceof Person)) {   //obj instanceof Person还有一种写法this.getClass()==obj.getClass()
            return false;//都不是一个物种, 不等的
        }
        
        Person other = (Person)obj;
        
        if(this.getId()==other.getId()){
            //如果两个人的身份证号码一样,我就认为这两个人是同一个人
            return true;
        }else {
            return false;
        }
    }    
}
public class TestMain {
    public static void main(String[] args){
    Person p1 = new Person();
    p1.setId(1);
    Person p2 = new Person();
    p2.setId(1);
    Person p3 = new Person();
    p3.setId(2);
    System.out.println(p1.equals(p2));//true
    System.out.println(p1.equals(p3));//false
        } 
}         

1. 什么是单例模式

单例模式指的是在应用整个生命周期内只能存在一个实例。单例模式是一种被广泛使用的设计模式。他有很多好处,能够避免实例对象的重复创建,减少创建实例的系统开销,节省内存。

2. 单例模式和静态类的区别

首先理解一下什么是静态类,静态类就是一个类里面都是静态方法和静态field,构造器被private修饰,因此不能被实例化。Math类就是一个静态类。

Math类: public static final double PI = 3.14159265358979323846;

知道了什么是静态类后,来说一下他们两者之间的区别:

1)首先单例模式会提供给你一个全局唯一的对象,静态类只是提供给你很多静态方法,这些方法不用创建对象,通过类就可以直接调用;

2)单例模式的灵活性更高,方法可以被override,因为静态类都是静态方法,所以不能被override

3)如果是一个非常重的对象,单例模式可以懒加载,静态类就无法做到;

那么时候时候应该用静态类,什么时候应该用单例模式呢?首先如果你只是想使用一些工具方法,那么最好用静态类,静态类比单例类更快,因为静态的绑定是在编译期进行的。如果你要维护状态信息,或者访问资源时,应该选用单例模式。还可以这样说,当你需要面向对象的能力时(比如继承、多态)时,选用单例类,当你仅仅是提供一些方法时选用静态类。

3.如何实现单例模式

1. 饿汉模式

所谓饿汉模式就是立即加载,一般情况下再调用getInstancef方法之前就已经产生了实例,也就是在类加载的时候已经产生了。这种模式的缺点很明显,就是占用资源,当单例类很大的时候,其实我们是想使用的时候再产生实例。因此这种方式适合占用资源少,在初始化的时候就会被用到的类。

public class SglDemo {
    
    //2、新建一个属性
    private static SglDemo sglDemo = new SglDemo();//饿汉模式,新建属性的时候,就初始化属性
    //1、将构造器私有化
    private SglDemo() {
        
    }
    //3、提供一个外部获取本类对象的公共方法
     public static SglDemo getInstence() {
         return sglDemo;
     }
}
public class TestMain {
    public static void main(String[] args){
             //无论获取多少次SglDemo类的对象,都是同一个对象
        //SglDemo sglDemo = new SglDemo();//这种方式错误
        SglDemo sglDemo1 = SglDemo.getInstence();
        SglDemo sglDemo2 = SglDemo.getInstence();
        System.out.println(sglDemo1 == sglDemo2);//true
        } 
}      

 

2. 懒汉模式

懒汉模式就是延迟加载,也叫懒加载。在程序需要用到的时候再创建实例,这样保证了内存不会被浪费。针对懒汉模式,我们暂时给出了1种最简单实现方式,有线程安全的问题,我们在学习多线程后,在完善这里的写法:

public class SglDemo2 {
    //2、新建一个本类类型的属性
    private static SglDemo2 sglDemo2 = null;//建立这个属性的是时候,就没有初始化
    
      //1、构造器私有化
    private SglDemo2() {}
    
    //3、对外提供获取本类对象的方法
    public static SglDemo2 getInstance() {
            if(sglDemo2 == null) {
                sglDemo2 = new SglDemo2();
            }
            return sglDemo2;
    }
}
public class TestMain {
    public static void main(String[] args){
         SglDemo2 sglDemo1 = SglDemo2.getInstance();
         SglDemo2 sglDemo2 = SglDemo2.getInstance();
         System.out.println(sglDemo1==sglDemo2);//true
        } 
}     

学到这里,你可能会对设计模式感到难以理解,单列模式其实就是一种最常用的设计模式。

设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。

现阶段的你,我的意见或者经验就是,不懂先记住,留待以后随着学习的知识越来越多,越来越深入后慢慢理解!

 

 

原文地址:https://www.cnblogs.com/jikebin/p/12458161.html