疯狂java


您现在的位置: 疯狂软件 >> 新闻资讯 >> 正文

Java基础之包装类,接口和内部类


 

   JAVA基础之包装类,static,final,abstract ,接口和内部类

  包装类:

  自jdk5之后,Java就提供了自动装箱与自动拆箱功能,大大简化了基本类型与其包装类对象之间的转换过程,当然装换过程中要注意类型的匹配。

  public class IntAndInteger

  {

  public static void main(String[] args)

  {

  //自动装箱

  Integer intObj = 5;

  Integer intObjj = 5;

  //自动拆箱

  int a = intObj;

  //包装类实现基本变量与字符串的转换

  String str = "123";

  //使用静态方法转换

  int b = Integer.parseInt(str);

  //使用构造器转换

  int c = new Integer(str);

  //多个重载的valueOf()方法,把基本类型转换为字符串

  String s = String.valueOf(b);

  String boo = String.valueOf(false);

  //基本类型或包装类加上“”,会自动转换为string类型

  String ss = a+"";

  String sss = intObj+"";

  //包装类实例可以与基本数值类型比较大小,用的是其包装的数值

  Integer i = new Integer(6);

  System.out.println(i > 5);

  //两个包装类的实例进行比较,只有两个包装类执行同一个对象,才相等

  System.out.println(intObj == intObjj); //true,都指向常量池中的5

  System.out.println(intObj == i);//false

  /*

  * 看下一个自动装箱例子,查看Integer的源码,将会看到如下代码:

  static final Integer[] cache = new Integer[256];

  static

  {

  for(int i=0;i

  cache[i] = new Integer(i-128);

  }

  从上面代码中可以看出,Integer把-128~127之间的整数自动装箱成Integer之后,存到一个一个数组中,

  所以每次装箱的整数在-128~127之间都是直接指向数组元素,而这个范围之外的整数自动装箱都会新创建一个Integer实例

  */

  Integer a1 = 2;

  Integer a2 = 2;

  System.out.println(a1 == a2); //true

  Integer b1 = 128;

  Integer b2 = 128;

  System.out.println(b1 == b2); //false

  //java7为所有的包装类都提供了静态的compare方法,比较基本类型的大小

  System.out.println(Boolean.compare(false, true)); //-1

  System.out.println(intObj.equals(a)); //true

  }

  }

  ==和equals的区别:

  ==比较数值类型的基本变量(不一定要求类型完全相同),只要变量的值相等,就返回true; ==比较引用类型变量,只有两个变量指向同一个对象时,才返回true ,==不能用于比较两个类型上没有父子关系的两个对象; equals()是Object的方法,一般子类都要重写该方法,其目的是比较两个引用变量指向的对象的内容是否相同,当然具体的判断条件由编程者自己规定。有些累已经重写了该方法,如String类,用于比较两个字符串的内容是否相同

  常量池专门用于管理在编译时被确定并保存在.class文件中的一些数据 “abc” 与 new String("abc")(实际创建两个String对象),常量池保证相同的字符串直接量只有一个

  static:

  [cpp] view plain copy print?

  /*

  * 1. static修饰的成员属于类成员,包括类变量,类方法,静态初始化块,内部类,static不能修饰构造器

  * 2. 静态变量,同一个类的所有实例共享同一块内存区

  * 3. 类方法,即使实例为null,依然可以访问类方法或变量

  * 4. 类成员(类方法,初始化块,内部类)不能访问实例成员(变量,方法,初始化块,内部类),反之可以

  * 5. 不能在方法中声明静态变量

  */

  public class ClassNumber

  {

  private static void test()

  {

  System.out.println("测试");

  }

  public static void main(String[] args)

  {

  ClassNumber classNumber = null; //=null,先进行初始化

  classNumber.test();

  }

  }

  final:

  final修饰变量

  [cpp] view plain copy print?

  /*

  * 1.final修饰成员变量,一旦获得了初始值,就不能再被重新赋值,final修饰类变量,只能在声明时或者静态代码块之一为其赋值;

  * final修饰实例变量,可以在声明时,非静态代码块,构造器三者之一为变量赋值

  * final修饰的变量必须由程序员显示的指定初始值,否则编译出错

  */

  /*

  * 2.final修饰局部变量

  * final修饰局部变量,要么在声明时初始化,要么在之后初始化一次

  * final修饰形参,调用该方法时会进行初始化,所有方法中不能对其进行赋值

  * 3.final修饰基本类型的变量时,需保证变量的值不能发生改变,修饰引用类型变量时,只需保证变量指向的地址空间

  * 不发生变化就可以,它指向的对象可以随意改变

  */

  /*

  * 4.可执行宏替换的final变量,用final修饰的变量,无论是类变量,实例变量,还是局部变量

  * 只要满足以下三个条件就可以当做直接量来看,进行宏替换,编译时把用到该变量的所有地方替换成对应值

  * 1>使用final变量修饰

  * 2>声明时指定了初始值

  * 3>变量的值可以在编译时确定下来

  * 注:除了为final变量赋值为直接量之外,如果被赋的表达式是最基本的算术表达式或字符串直接量连接运算,

  * 没有使用普通变量,调用方法,java编译器同样会把这种变量作为“宏变量”

  */

  public class FinalTest

  {

  public static int h = 0;

  public static void main(String[] args)

  {

  final String str = "好人";

  //str = "大好人"; 错误

  final int a = 5;

  System.out.println(a); //对程序来说变量a根本不存在,相当于执行System.out.println(5)

  /*

  * 宏变量

  */

  final int b = 2 ; //宏

  final String str1 = "Linux程序设计"; //宏

  final String str2 = "Linux"+"程序设计"; //宏

  String str3 = "程序设计"; //普通变量

  String str4 = "Linux" + str3 ; //使用普通变量,不是宏

  System.out.println(str1 == str2); //true

  System.out.println(str1 == str4); //false

  }

  public void test(final int a)

  {

  //a = 5; 不能再次赋值

  }

  }

  final修饰方法:

  [cpp] view plain copy print?

  /*

  * 1.子类不能重写父类中使用final修饰的方法,否则编译出错

  * 2.当父类中的方法使用private final修饰的时候,该方法只在类中可见,子类并不能继承到该方法

  * 所以完全可以在子类中再次定义一个与其返回值,方法名,参数完全相同的方法

  * 3.final修饰的方法只是不能被重写,但是可以被重载

  */

  public class FinalMethodTest

  {

  public final void test(){}

  private final void test1(){}

  }

  class ChildTest extends FinalMethodTest

  {

  //public void test(){} 编译出错

  public void test1(){} //不出错

  public void test(int a){}; //可以

  }

  final修饰类:

  [cpp] view plain copy print?

  /*

  * 1.使用final修饰的类不可以有子类

  * 2.不可变类:即创建了该类的实例后,该实例的实例变量不可改变,8个包装类与String都是不可变类

  * 3.自定义不可变类遵循的原则:

  * 1>使用private final 修饰成员变量

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

  * 3>仅为变量提供get方法,我无需提供set方法,因为普通方法无法改变final变量

  * 4>有需要的话重写equals()和hashCode()方法

  * 下面创建一个不可变类,不可变类的实例一直处在初始化状态,对其的控制会变的简单

  */

  public class FinalClassTest

  {

  private final String detail;

  private final String post;

  public FinalClassTest()

  {

  this.detail="";

  this.post="";

  }

  public FinalClassTest(String detail,String post)

  {

  this.detail=detail;

  this.post=post;

  }

  public String getDetail() {

  return detail;

  }

  public String getPost() {

  return post;

  }

  public boolean equals(Object obj)

  {

  if(obj == this)

  {

  return true;

  }

  if(obj!=null && obj.getClass() == FinalClassTest.class)

  {

  FinalClassTest object = (FinalClassTest)obj;

  if(this.getDetail().equals(object.getDetail()) && this.getPost().equals(object.getPost()))

  return true;

  }

  return false;

  }

  public int hashCode()

  {

  return this.getDetail().hashCode()+this.getPost().hashCode()*11;

  }

  }

  [cpp] view plain copy print?

  /*

  * 设计具有其他类的引用的不可变类

  */

  class Name

  {

  private String firstName;

  private String lastName;

  public Name(){}

  public Name(String firstName,String lastName)

  {

  this.firstName=firstName;

  this.lastName = lastName;

  }

  public String getFirstName() {

  return firstName;

  }

  public void setFirstName(String firstName) {

  this.firstName = firstName;

  }

  public String getLastName() {

  return lastName;

  }

  public void setLastName(String lastName) {

  this.lastName = lastName;

  }

  }

  public class FinalClassTest2

  {

  private final Name name; //因为name对象是可以改变的,这与设计不可变类初衷相违背

  public FinalClassTest2(Name name)

  {

  this.name=new Name(name.getFirstName(),name.getLastName()); //使用不同的引用

  }

  public Name getName() {

  return name;

  }

  public static void main(String[] args)

  {

  Name name = new Name("孙","悟空");

  FinalClassTest2 f = new FinalClassTest2(name);

  name.setFirstName("猪");

  System.out.println(f.getName().getFirstName()); //孙 不会被改变 达到不可变类的效果

  }

  }

  abstract:

  [cpp] view plain copy print?

  package abstractpack;

  /*

  * 1.抽象类抽象方法的规则

  * 1>抽象类的抽象方法都要用abstract关键字来修饰,包含抽象方法的类一定是抽象类,抽象类可以不包含抽象方法

  * 2>抽象类不能被实例化,不能使用new关键字来创建抽象类的对象,即使抽象类不包含抽象方法

  * 3>和普通类一样,抽象类中可以声明成员变量,方法,构造器,初始化块,内部类,抽象类的构造器不是用来创建实例,而是供子类调用

  * 4>只要类中包含抽象方法,包括自己定义的抽象方法,没有完全实现父类或接口中的抽象方法,则该类必须声明为抽象类

  * 2.final和abstract永远不能同时使用

  * 3.abstract不能修饰变量,局部变量,构造器

  * 4.abstract和static不能同时修饰一个方法,但是可以同时修饰内部类

  * 5.abstract方法不能为private权限

  */

  /*

  * 模板模式:

  * 1.抽象父类只定义需要使用的某些方法,把不能实现的部分抽象成抽象方法,交给子类去实现

  * 2.父类中可能包含调用其他方法的方法,被调用的方法可能需要具体的子类去实现,见下例:

  */

  class father

  {}

  abstract class SpeedMeter extends father //可以继承普通类

  {

  private double turnRate;

  public SpeedMeter()

  {

  }

  public abstract double getRadius();

  public void setTurnRate(double turnRate)

  {

  this.turnRate = turnRate;

  }

  public double getSpeed()

  {

  return Math.PI*2*getRadius()*turnRate; //调用需要子类实现的抽象方法

  }

  }

  public class CarSpeedMeter extends SpeedMeter

  {

  public double getRadius()

  {

  return 2.0;

  }

  public static void main(String[] args) //入口方法必须放在public修饰的类里!!!

  {

  CarSpeedMeter speed = new CarSpeedMeter();

  speed.setTurnRate(3.0);

  System.out.println(speed.getSpeed());

  }

  }

  interface:

  [cpp] view plain copy print?

  package interfacepack;

  /*

  * 1.因为接口定义的是一种规范,所有接口里不能包含构造器和初始化块。接口里可以包含变量(public static final,在定义是指定默认值),

  * 方法(public abstract抽象方法,static类方法,default默认方法,java8以上才支持类方法默认方法,必须由方法实现),public static内部类(枚举,内部接口)

  * 2.接口里定义的是多个类公共的规范,所以所有成员都用public修饰

  */

  /*

  * 接口和抽象类:

  * 共同点:1.都不能被实例化

  * 2.都可以包含抽象方法

  * 不同点:

  * 设计目的:接口体现的是一种规范。对于接口的实现者而言,接口规定了实现者必须向外界提供哪些服务,对于接口的调用者而言,它规定了调用者可以得到哪些服务,以及怎样得到

  * 当在一个程序中使用接口时,它是多个模块之间耦合的标准;当在多个应用程序之间使用接口时,它是多个程序之间通信标准,一个系统中的接口不应该经常改变

  * 抽象类体现的是一种模板式的设计思想,可以把它看成中间产品,防止子类设计的随意性

  * 用法上的差别:

  * 1>不能为普通方法提供方法实现,只能是抽象方法,默认方法,类方法,而抽象方法可以

  * 2>不能定义普通成员,只能是静态常量

  * 3>不能包含构造器

  * 4>不能包含初始化块

  * 5>一个类可以实现多个接口,但是只能继承一个抽象类

  *

  */

  public interface InterfaceTest

  {

  int Max_mine = 50 ; //默认使用public static final修饰,需在定义时初始化

  void out(); //普通方法默认使用public abstract修饰

  default void print(Object obj) //默认方法,使用default修饰,必须提供方法体,不能用static修饰

  {

  System.out.println(obj);

  }

  static void write(Object obj) //类方法,使用static修饰,必须提供方法体,不能用default修饰,可由接口直接调用

  {

  System.out.println(2);

  }

  }

  面向接口编程:

  [cpp] view plain copy print?

  package interfacepack;

  /*

  * 面向接口编程

  * 1.简单工程模式:

  * 假设需要为Computer类组装一个输出设备,这时,我们让computer类耦合输出设备的父接口Output,以后不论电脑需要的输出设备

  * 怎样改变,电脑类的代码都不需要改变.电脑类不需要产生输出设备的实例,而交给工厂类来完成

  * 这样即可把所有生产Output对象的逻辑都集中在OutputFactory中,而所需要使用Output对象的类只需要与接口耦合,而不是

  * 与具体的实现类耦合

  */

  public class Computer

  {

  private Output output;

  public Computer(Output output)

  {

  this.output = output;

  }

  //调用输出设备的方法...

  }

  //定义一个工厂类,生产输出设备

  class Factory

  {

  public Output getOutput()

  {

  return new Printer();

  //return new BetterPrinter();

  }

  }

  interface Output

  {

  //一些作为输出设备需要提供的方法

  //...

  }

  class Printer implements Output

  {

  //实现接口中定义的抽象方法...

  }

  class BetterPrinter implements Output

  {

  //实现接口中定义的抽象方法...

  }

  /*

  * 2.命令模式:假设某个方法要完成某项功能,但是这个功能的实现必须等到执行时才能确定,这时可以把命令作为参数传递进去

  * 假设需要对数组进行操作

  */

  //定义Command接口来封装处理行为

  interface Command

  {

  void process(int[] target);

  }

  //处理数组的类,可以在调用该类的process方法处理数组的时候,动态的传入Command接口的不同的实现类对象

  //以实现对数组的不同类型的操作

  public class ProcessArray

  {

  public void process(int[] target,Command cmd)

  {

  cmd.process(target);

  }

  内部类:

  成员内部类

  [cpp] view plain copy print?

  package innerclasspack;

  /*

  * 1.成员内部类是一种与成员变量,成员方法,初始化块相似的类成员,而局部内部类和匿名内部类不是类成员

  * 2.外部类的上一级程序单元是包,所以他只有两个作用域,一个是包(缺省),一个是公开(public),而内部类的外层是外部类,它有4个作用域,

  * 类内(private),包(缺省),父子类(protected),公开(public)

  * 3.非静态内部类:

  * 1>非静态内部类可以直接访问外部类中的私有成员,在其内部保存了一个对外部类对象的引用

  * 2>如果外部类想要调用非静态内部类中的实例成员(包括private),则必须先创建内部类的对象,因为外部类的对象存在时,不一定存在内部类的对象,而内部类的对象存在时,

  * 外部类的对象一定存在

  * 3>不允许在外部类的静态成员中直接使用非静态内部类,包括创建对象,使用变量

  * 4>java不允许在非静态内部类中定义静态成员,包括变量,方法,静态初始化块

  * 4.静态内部类:

  * 1>static关键字的作用是把类的成员变成类相关,而不是实例相关,因此static关键字不能修饰外部类,但是可以修饰内部类

  * 2>静态内部类既可以包含静态成员,也可以包含非静态成员,静态内部类只能访问外部类的实例成员,即使是静态内部类的实例方法,也不能访问外部类的实例成员

  * 因为静态内部类是外部类类相关的,当静态内部类的对象存在时,并一定存在被它寄生的外部类的对象,只持有对外部类的类引用

  * 3>静态内部类时外部类的静态成员,所以外部类的所有方法,初始化块都能使用静态内部类创建对象,定义变量。但是依然不能直接访问其内部的成员,可以

  * 通过类名或者创建内部类的对象来调用

  * 4>接口里定义的内部类只能是public static

  */

  /*

  * 使用内部类:定义类的主要作用就是定义变量,创建实例,被继承

  * 1.在外部类内部使用内部类:与使用普通类没有太大区别,只要注意别在外部类的静态成员中使用非静态内部类

  * 2.在外部类外使用非静态内部类:

  * 1>若想要在外部类之外使用内部类,则不能使用private

  * 缺省:可以被同一包中的其他类访问

  * protected:可以被与外部类处于同一个包中的以及外部类的子类访问

  * public:被所有类访问

  * 2>创建内部类对象的格式:OuterInstance.new InnerConstructor(),即要先创建外部类的实例,在由其调用内部类的构造方法

  * 3>创建内部类的子类时,其子类也需要包含对外部类对象的引用,所以子类的构造方法中应该传入对外部类对象的引用

  * 4>非静态把内部类的子类可以是一个外部类,但是需要保存对外部类对象的引用

  * 3.在外部类以外使用静态内部类:

  * 1>创建实例:new OuterClass.InnerConstructor();

  * 2>声明子类:class SubStaticClass extends StaticOut.StaticIn{},要使用父类的构造器,外部类像是包空间

  * 3>既然内部类时外部类的成员,是否可以定义外部类的子类,重写其内部类成员??答案是不可以,内部类的类名实际是把外部类类名作为命名空间

  * 子类外部类,命名空间改变,内部类也不再是同一个内部类,即使类名相同

  */

  //访问非静态内部类

  class Out

  {

  class In //同一个包中可以访问

  {

  public In(String name)

  {

  System.out.println(name);

  }

  }

  }

  public class CreateInnerInstance

  {

  public static void main(String[] args)

  {

  Out.In in = new Out().new In("lee"); //创建实例

  }

  }

  class SubClass extends Out.In

  {

  public SubClass(Out out) //子类包含对外部类对象的引用

  {

  out.super("hello");

  }

  }

  //访问静态内部类

  class StaticOut

  {

  static class StaticIn

  {

  public StaticIn(String name)

  {

  System.out.println(name);

  }

  }

  }

  public class CreateInnerInstance

  {

  public static void main(String[] args)

  {

  StaticOut.StaticIn in = new StaticOut.StaticIn("lee"); //创建实例

  }

  }

  class SubStaticClass extends StaticOut.StaticIn

  {

  public SubStaticClass(String name) //使用父类的构造器

  {

  super(name);

  }

  }

  局部内部类和匿名内部类:

  [cpp] view plain copy print?

  package innerclasspack;

  /*

  * 局部内部类:

  * 1>因为局部内部类不能在方法之外的地方使用,所以不能使用访问控制符和static修饰

  * 2>如果需要使用局部内部类定义变量,创建实例,或者派生子类,只能在方法中进行

  * 3>编译之后生成三个.class文件:JuBuInnerClass.class , JuBuInnerClass$1InnerBase.class ,

  * JuBuInnerClass$S1ubInneBase.class,数字是 为了区分方法中同名的内部类

  * 匿名内部类:

  * 1>匿名内部类适合那种只需要使用一次的类,创建匿名内部类时会立即创建该类的实例,之后这个类定义立即消失

  * 2>语法格式:new 实现接口()|父类构造器(实参){类体},匿名内部类必须实现一个接口或者继承一个类,而且只能是一个。

  * 3>匿名内部类不能是抽象类,不能有构造器,可以有初始化块

  * 4>当通过实现接口来创建匿名内部类时,其只有一个隐式的无参构造器,所以new关键字后面不能传入参数值;如果通过继承父类来创建内部类,

  * 其具有与父类相似的构造器,即构造器的形参列表相同。

  * 5>在java8之前,java要求被局部内部类和匿名内部类访问的局部变量必须是final类型,java8之后这个限制被取消了,如果局部变量

  * 被匿名内部类访问,会自动为其加上final修饰符。

  */

  //局部内部类

  public class JuBuInnerClass

  {

  public static void main(String[] args)

  {

  class InnerBase

  {

  int a;

  public InnerBase(){}

  }

  class SubInneBase extends InnerBase //方法中进行

  {

  int b;

  }

  SubInneBase sub = new SubInneBase();

  sub.a = 1;

  sub.b = 2;

  System.out.println("子类的变量值a:"+sub.a+" b:"+sub.b);

  }

  }

  //匿名内部类

  interface Product

  {

  public double getPrice();

  }

  public class JuBuInnerClass

  {

  public void test(Product p)

  {

  System.out.println("商品的价格:"+p.getPrice());

  }

  public static void main(String[] args)

  {

  JuBuInnerClass j = new JuBuInnerClass();

  j.test(new Product() //商品的价格:11.11

  {

  public double getPrice()

  {

  return 11.11;

  }

  });

  }

  }

  //调用局部变量

  interface A

  {

  void test();

  }

  public class JuBuInnerClass

  {

  public static void main(String[] args)

  {

  int age = 8;

  A a = new A()

  {

  public void test()

  {

  //age = 7; //使用的时候必须按照final变量的使用规则

  System.out.println(age);

  }

  };

  a.test(); //8

  }

  }