面向对象下
1.1 java8的增强那包装类
为了解决8种基本数据类型的变量不能单程Object类型的变量使用的问题,java提供了包装类的概念,为8中基本数据类型分别定义了相应的引用类型,称之为基本数据类型的包装了类。(Objcet类是所有类的父类)
public class AutoBoxingUnboxing
{
public static void main(String[] args)
{
//直接把一个基本类型变量付给Integer对象
Integer inObj=5;
//直接把bloolean类型变量赋给一个Object类型的变量
Object boolObj=true;
//直接把一盒Integer对象赋给int类型变量
int it=inObj;
if(boolObj instanceof Boolean) // instance它的作用是判断其左边对象是否为其右边类的实例
{
//先把object对象强制类型转换为Boolean类型,再赋给boolean变量
boolean b=(Boolean)boolObj;
System.out.println(b);
}
}
}
** java7为所有的包装类都提供了一个静态的compare,用来比较两个基本类型值得大小
例如 Boolean.compare(true,false)
1.2处理对象
打印对象和toString方法
==和equals方法
重要:==只比较数值 当是两个引用类型不同的对象比较时,返回false
很多时候程序判断两个引用变量是否相等时,并不严格要求两个引用变量指向同一个对象。那就可以用Stiing对象的equals方法来判断
//String中重写Object类的equals方法的源码
public boolean equals(Object anObject)
{
if(this==anObject)
{
return true; //如果是同一个实例 那么return true
}
if(anObject instanceof String)//看是否是tring的子例
{
String anotherString=(String)anObject;
int n=value.length;
if(n==anotherString.value.length) //如果长度相等
{
//分别放入数组中就行比较
char v1[]=value;
char v2[]=abotherString.value;
int i=0;
while(n-- !=0)
{
if(v[i]!=v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
1.3类成员
单例模式例子
class singleton
{
//使用一个类变量来缓存曾经创建的实例
private static Singleton instance;
//对构造器使用private修饰,隐藏该构造器
private Singleton(){}
//提供一个静态方法,用于返回Singleton实例
//该方法可以加入自定义控制,保证只产生一个Singleton对象
public static Singleton getInstance()
{
//如果instance为null,则表明还不曾创建Singleton对象
//如果instance不为null,则表明已经创建了Singleton对象
//将不会重新创建新的实例
if(instance==null)
{
//创建一个Singleton对象,并将其缓存起来
instance=new Singleton();
}
return instance;
}
}
public class SingletonTest
{
public static void main(String[] args)
{
//创建Singleton对象不能通过构造器
//只能通过getInstance方法来得到实例
Singleton s1=Singleton.getInstance();
Singleton s2=Singleton.getInstance();
System.out.println(s1==s2); //输出true,因为只有一个实例被创建
}
}
1.4final修饰符
final关键字用于修饰类、变量个方法,一旦被修饰将不能改变
** final修饰的成员变量必须由程序员显式的指定初始值
1.4.1 final成员变量
public class FinalVariableTest
{
//定义成员变量时指定默认值,合法
final int a=6;
//下面变量将在构造器或初始化块中分配初始值
final String str;
final int c;
final static double d;
//既没有指定默认值,也没有再初始化快,构造器中指定初始值
//下面定义的ch实例变量是不合法的
//final char ch;
//初始化块,可对没有指定默认值的实例变量指定初始值
{
//在初始化块中为实例变量指定初始值,合法
str="Hello";
//定义a实例变量时已经指定了默认值
//不能为a重新赋值,因此下面赋值语句非法
//a=9;
}
//静态初始化块,可对没有指定默认值的类变量指定初始值
static
{
d=5.6;
}
//构造器,可对既没有指定默认值,又没有在初始化块中
//指定初始值的实例变量指定初始值
public FinalVariableTest()
{
//如果已经在初始化块中已经对str指定的初始值
//那么在构造器中不能对final变量重新复制,
//str=“java” 语句非法
c=5;
}
public void changeFinal()
{
//普通方法不能为final修饰的成员变量赋值
//d=1.2;
//不能在普通方法中为final成员变量指定初始值
//ch=“a”;
}
}
**与普通成员变量不同的是,final成员变量必须由程序员显式初始化,系统不会对final成员进行隐式初始化
1.4.2 final修饰局部变量,
可以定义时指定默认值,也可以不指定默认值,没有指定默认值的可以在后面代码中赋值,但只能一次,不能重新赋值
1.4.3 final修饰的基本类型变量和引用类型变量区别
当修饰基本类型变量时,不能对变量重新赋值,引用类型变量只要保证引用的地址不改变就行。
1.4.4 可执行“宏替换”的final变量
**final修饰符的一个重要用途就是定义“宏变量”。当定义final变量时就为该变量指定了初始值,而且该初始值可以在编译时就确定下来,那么这个final变量本质上就是一个“宏变量”,编译器会把程序中所有用到该变量的地方直接替换成该变量的值。
注意:对于final实例变量而言,只有在定义该变量时指定初始值才会有“宏变量”的效果。
1.4.5 final方法
final修饰的方法不可被重写,不会希望子类重写父类的某个方法,则可以使用final修饰该方法
1.4.6 final类
final修饰的类不可以有子类。
1.1.7 不可变类、
不可变类的意思是创建该类的实例后,该类的实例是不可改变的。java提供的8个包装类和java.lang.String类都是不可变类
创建自定义的不可变类,遵循如下规则:
1 使用private和final修饰符来修饰该类的成员变量
2提供带参数的构造器,用于根据传入参数来初始化类里的成员变量
3 仅为该类的成员变量提供getter方法,因为普通方法无法修改final修饰的成员变量。
4 如果有必要,冲洗人Object类的hashCode()和equals()方法
注意:当使用final修饰引用变量时,仅仅表示引用变量不能被赋值,但是引用变量所指向的对象依然可改变。这就会产生问题,当创建不可变类时,如果包含的成员变量的类型是可变的,那么其对象的成员变量的值依然可以改变,那么这个不可变类就是失败的。
1.4.8 缓存实例的不可变
如果程序经常使用相同的不可变类,那么应该考虑缓存这种不可变类,用于节省开销。
缓存的实现方式很多,现在提供一个数组作为缓存池,从而实现一个缓存实例的不可变类
class CachImmutale
{
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 CachImmutale(String name)
{
this.name=name;
}
public String getName()
{
return name;
}
public static CachImmutale valueOf(String name)
{
//遍历已缓存的对象
for(int i=0;i<MAX_SIZE;i++)
{
if(cache[i]!=null)&&cach[i].getName().equals(name))
{
return cache[i];
}
}
//如果缓存池已经满
if(pos==MAX_SIZE)
{
//把缓存的第一个对象覆盖,
cache[0]=new CacheImmutale(name);
//吧pos设为1
pos=1;
}
else
{
//把新创建的对象缓存起来,pos加1
cache[pos++]=new CacheImmutale(name);
}
return cache[pos-1];
}
public boolean equals(Object obj)
{
if(this==obj)
{
return true;
}
if(obj!=null&&obj.getClass()==CacheImmutale.class)
{
CacheUmmulate ci =(CacheImmutale)obj;
return name.equals(ci.getName());
}
return false;
}
public int hashCode()
{
return name.hashCode();
}
}
public class CacheImmutaleTest
{
public static void main(String[] args)
{
CacheImmulate c1=CacheImmutale.valueOf("hello");
CacheImmulate c2=CacheImmutale.valueOf("hello");
System.out.print(c1==c2) //true
//c1和c2是同一个对象
}
}
6.5 抽象类
注意:有抽象方法的类只能被定义成抽象类,抽象类里可以没有抽象方法
6.5.1 抽象方法和抽象类
两者规则如下:
1 抽象类必须使用abstract修饰符来修饰
2 抽象类不能被实例化,只能被继承
3 抽象类中的构造器不能用于创建实例,主要用于被其子类调用
同时注意 abstract就是要让子类继承的 不能用private修饰
6.5.2 抽象类的作用
抽象类属于那种从子类抽象出来的父类,子类以抽象类为模板可以避免子类设计的随意性。
6.6 java8 改进的接口
功能类似于抽象类 但是是更彻底的抽象,java8对接口进行了改进,允许在接口中定义默认方法,默认方法可以提供方法实现。
接口的目的就是要让规范和实现分离 而接口就是这个规范。接口里通常定义的是一组公用方法。
注意:接口只能定义接口,不能定义类,接口里方法只能是抽象方法,类方法或默认方法。只能用public修饰符
同时注意:不管是否使用public static final修饰符,接口里的成员变量总是使用这三个修饰符来修饰。
系统将自动的为普通方法增加abstract修饰符。接口里的普通方法不能有方法实现,但是类方法,默认方法都必须有方法实现。
默认方法必须使用default修饰,类方法必须使用static修饰
从某个角度来看,接口被当成一个特殊的类,因此一个java源文件最多只能有一public接口,如果一个源文件里定义了一个public 接口,则该源文件的主文件名必须与接口名相同。
接口支持多继承,也就是一个类可以实现多喝接口
一个类实现了一个接口就要实现接口里全部的抽象方法。
6.6.6面向接口编程
6.7内部类
把一个类放在另一个类的内部定义,这个定义在内部的类称为内部类
好处:1 封装性更好
2 内部类成员可以直接访问外部类私有数据
3 匿名内部类适合用于创建那些仅需要一次使用的类
4 非静态内部类不能拥有静态成员
6.7.1 非静态内部类
没有static修饰的就是非静态内部类
注意:非静态内部类的成员可以访问外部类的private成员,但是反过来不成立。非静态内部类的成员只在非静态内部类范围内是可知的,并不能被外部类直接使用。
静态成员不能访问非静态成员,因此main函数不能直接访问内部类,会出错
public class StaticTest
{
//定义了一盒非静态内部类
private class In{}
public static void main(String[] args)
{
new In(); // 报错,因为静态成员不能访问非静态方法
}
}
6.7.2 静态内部类
使用static修饰的内部类,就是静态内部类
static不可修饰外部类(因为外部类上一级是包),只能修饰内部类
public class StaticInnerClassTest
{
private int prop1=5;
private static int prop2=9;
//静态内部类里可以包含静态成员
static class StaticInnerClass
{
private static int age;
publuc void accessOutprop()
{
//代码错误 静态内部类无法访问外部类的实例变量
System.out.print(prop1)
// 代码正常,
System.out.print(prop2)
}
}
}
注意:因为使用静态内部类比使用非静态内部类要简单很多,因此程序员使用内部类的时候,应优先考虑使用静态内部类
6.7.4 局部内部类
把一个内部类放在方法里定义,就是局部内部类,局部内部类只在方法里有效
6.7.5 java8改进的匿名内部类
匿名内部类适合那种只需要异地使用的类,创建匿名内部类时会立刻创建一盒该类的实例,这个类定义立刻消失,匿名内部类不能重复使用
语法
new 实现接口() | 父类构造器(实参列表)
{
}
从代码可以看出匿名内部类必须继承一个父类,或者实现一个接口。
最常用的匿名内部类的方式是需要创建某个接口类型的对象
Interface Priduct
{
public double getProce();
public String getName();
}
public class AnonymoueTest
{
public void test(Product p)
{
System.out.println("购买了一个"+p.getName()+"
,花掉了"+p.getPrice());
}
public static void main(String[] args)
{
AnnonyTest ta=new AnonymousTest();
//调用test()方法时,需要传入一个Product参数
//此处传入其匿名实现类的实例
ta.test(new Product()
{
public double getPrice()
{
return 568.5;
}
public String getName()
{
return "施维赫夫";
}
}
);
}
}
上面代码中Product只是一个接口,无法直接创建对象。如果Product接口实现类需要重复使用,则应将该实现类定义成一个独立类,如果这个Product接口实现类只需使用一次,则可使用上面程序的方式,定义一个匿名内部类
java8更改如下:如果局部变量被匿名内部类访问,那么该局部变量相当于自己动用了final修饰
java8新增的Lamba表达式
Lamba表达式支持将代码块作为方法参数,Lamba表达式允许使用更简洁的代码创建只有一个抽象方法的接口(这种接口被称为函数式接口)的实例。