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

Posted 蜡笔大新001

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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.length;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

/*
 * 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修饰变量

/*
 * 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修饰方法:

/*
 * 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修饰类:

/*
 * 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;
	}
}

/*
 * 设计具有其他类的引用的不可变类
 */
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

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

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);
	}
}

面向接口编程

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);
	 }


内部类

成员内部类

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);
	}
}

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

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
	}
}

这部分是java基础中很重要的部分,也是面试官的最爱,需要熟练掌握。


以上是关于JAVA基础之包装类,static,final,abstract ,接口 和 内部类的主要内容,如果未能解决你的问题,请参考以下文章

一脚踩进java之基础篇19——面向对象 (final,static关键字)

一脚踩进java之基础篇19——面向对象 (final,static关键字)

Java之static与final关键字

JAVA零基础小白学习教程之day09-内部类&权限&final&静态

JAVA零基础小白学习教程之day09-内部类&权限&final&静态

java基础复习:final,static,以及String类