包装类:
自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
}
}
==比较数值类型的基本变量(不一定要求类型完全相同),只要变量的值相等,就返回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基础中很重要的部分,也是面试官的最爱,需要熟练掌握。