面向对象(下)
6.1 java8增强的包装类
int Integer
char Character
其他的都是直接首字母变大写
可以自动装箱,自动拆箱。
如果int想变成Integer,直接赋值给一个Integer 变量就行。
如果Integer想变成int,直接赋值给一个int变量就行。
基本类型和字符串之间的转换方法
- 字符串转换成基本类型
int i=Integer.parseInt(str);
或者
int i=new Integer(str); - 基本类型转换成字符串
String flStr=String.valueOf(float变量);
或者
String flStr=float变量+“”;
包装类比较大小
- 如果直接给integer数字,如果数字在-128-127之间,则两个数字相等,包装类相等。
- 如果用new integer(2)赋值,则必须两个包装类指向同一个对象才相等。
- 有一个Integer.compare(a,b)可以直接用来比较包装类大小.
a>b 返回1
a=b 返回0
a
6.2 处理对象
6.2.1 打印对象和toString方法
toString方法
toString () 是Object类里面的一个实例方法,所有对象都具有这个方法。
系统自带的tostring“类名+@+hashcode”
可以自己重写这个方法。
6.2.2 ==和equals方法
“==”
- 对于基本类型的数值类型(包括char),只要两个变量的值相等,返回true。
- 对于引用类型,必须指向同一个对象,才返回true。
注意:“==“不能用于比较没有父子关系的两个对象。
”hello“直接量和new String (“hello”)区别
直接使用hello这种可以在编译时候计算出来的字符串值,jvm会用常量池来管理。jvm会保证相同的字符串直接量只有一个,不会产生多个副本。
第二种,jvm会先用常量池来管理直接量,再调用string构造器来创建 i 个新的string对象。新创建的对象保存在堆中
equals()
equals()方法可以用来判断字符串的内容是否相等。
但是当用于对象时,仍然必须是指向同一个对象才能够返回true。
一般需要重写equals()。
- 首先判断是否是同一个对象
- 如果不是同一个对象,在判断是否是同一个类型
- 如果是的话,把object转化为目标类型,采用equals方法比较内容。
6.3 类成员
6.3.1 理解类成员
类成员就是用static修饰的成员变量,方法,初始化块,内部类的统称。能通过对象访问这些类成员。即使对象是null 。类成员不能访问实例成员,因为类成员的作用域更大,可能类成员还存在,但是成员变量已经不存在了,所以不能够访问实例成员。
6.3.2 单例类
一个类始终只能创建一个实例,称为单例类。
如果不想让别的类轻易的创建该类的对象,就需要把类的构造函数设置成private,但是这个时候就需要有一个方法来创建一个对象,由于使用这个方法的时候还没有对象,因此需要给方法是static的。
同时。为了满足只能创建一个对象,则必须把已经创建的对象保存起来,每次创建对象之前都检查一下是否只有一个对象。由于存储对象的成员变量需要通过上面的static方法调用,因此需要设置为static。
package demo6;
public class Singleton {
//定义一个类变量用来保存创建出来的对象
private static Singleton instance;
private Singleton(){}
//定义一个能够返回对象的方法
public static Singleton getSingleton()
{
if(instance==null)
{
instance=new Singleton();
}
return instance;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Singleton s1=Singleton.getSingleton();
Singleton s2=Singleton.getSingleton();
System.out.print(s1==s2);
}
}
6.4 final修饰符
final修饰变量时候表示该变量一旦获得了初始值,就无法再改变。
6.4.1 final成员变量
类变量:在静态初始化块或者声明该变量时初始化
实例变量:在普通初始化块,或者声明该变量时候,或者构造器。如果已经在普通初始化块赋值,则不可以再在构造器中赋值。
注意⚠:普通方法不能访问final修饰的成员变量。
final修饰的成员变量必须程序员自己显示初始化,系统不会默认赋值。
6.4.2 final局部变量
因为系统不会给局部变量初始化,因此在用final修饰的局部变量不一定非由程序员显示初始化。
方法中的形参不能够在方法里面初始化,因为形参的初始化是在被调用的时候由实参传入的。
6.4.3 final修饰基本类型变量和引用类型变量的区别
final修饰基本类型变量,就不能更改值了。如果是引用类型,则只要不改变地址就行,里面的内容可以随意
package demo6;
import java.util.Arrays;
class Person1
{
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Person1() {}
public Person1(int age)
{
this.age=age;
}
}
public class FinalReferenceTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
//可以无限的更改数组里面的内容,但是不能够更改数组的地址
final int[] iArr={3,4,5,6};
iArr[2]=19;
System.out.print(Arrays.toString(iArr));
Arrays.sort(iArr);
System.out.println(Arrays.toString(iArr));
//iArr=null;
Person1 p1=new Person1();
p1.setAge(7);
//这里竟然可以更改,,,,,不懂不懂
p1=null;
System.out.print(iArr);
}
}
6.4.4 可执行“宏替换”的final变量
变量满足三个条件,则变量相当于一个直接量
- final修饰
- 在定义的时候指定了初始值(只有在定义的时候赋值才有这种效果)
- 值可以在编译的时候确定下来
6.4.5 final 方法
final方法不能够被重写。如果父类的方法不希望子类重写,只要加上final 就好。
但是父类的private 是不会被子类继承的,因此也不会有重写这个说法。因此如果父类的private方法被final了,子类仍然可以写一个一样的方法。
6.4.6 final类
不能有子类的类
6.4.7 不可变类
不可变类是创建该类的实例后,实例变量不能够更改。
创建自定义的不可变类方法:
1,类的成员变量用private和final修饰
2,提供带参数的构造函数,用于根据传入参数来初始化类的成员变量
3,该类仅有getter
4,重新定义equals()和hashcode()
package demo6;
public class Address {
private final String detail;
private final String postCode;
public Address()
{
this.detail="";
this.postCode="";
}
public Address(String detail,String postCode)
{
this.detail=detail;
this.postCode=postCode;
}
public String getDetail()
{
return detail;
}
public String getPostCode()
{
return postCode;
}
public boolean equals(Object obj)
{
if(obj==this)
{
return true;
}
if(obj!=null&&obj.getClass().equals(Address.class))
{
Address AddObj=(Address)obj;
if(AddObj.getDetail().equals(this.getDetail()))
{
return true;
}
}
return false;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
}
}
当创建不可变类的时候,如果成员变量里面有引用类型,则很可能创建出一个可变类,因为成员变量的内容可以更改。必须采用一些其他的方法,才能创建真正的不可变类。
6.4.8 缓存实例的不可变类
缓存实例的不可变类,是因为有的时候一个对象的某个成员被多次引用,为了节省开销,可以把它缓存起来。下边是把它缓存在数组里面,如果缓存里面已经有了,就直接返回实例,如果没有,就新建实例加进去。
package demo6;
//这个类主要是弄一个数组,然后缓存string数据
class CacheInnutale
{
private static int MAX_SIZE;
private static CacheInnutale[] cache=new CacheInnutale[MAX_SIZE];
//pos为什么要是static
private static int pos=0;
private final String name;
public String getName() {
return name;
}
private CacheInnutale(String name)
{
this.name=name;
}
// 使用数组缓存已有的实例,并且记录缓存的位置
public static CacheInnutale valueOf(String name)
{
//遍历已经缓存的对象,如果有相同的,直接返回缓存的实例
for(int i=0;i
6.5 抽象类
抽象方法是只有方法的签名,没有方法的实现
6.5.1 抽象方法和抽象类
抽象方法和抽象类必须使用abstract修饰,有抽象方法的类一定是抽象类。
规则:
- 抽象方法不能有方法体
- 抽象类不能够被实例化
- 抽象类的构造函数不能够用来创造实例,主要用于被子类调用
- 含有抽象方法的类(包括直接定义了一个抽象方法;继承了一个抽象父类,但是没有完全实例化父类包含的抽象类;或是实现了一个接口,但是没有完全实例化接口包含的抽象方法)
public class Triangle extends Shape{
//定义三角形的三边
private double a;
private double b;
private double c;
public Triangle(String color,double a,double b,double c)
{
super(color);
this.sideSides(a,b,c);
}
public void sideSides(double a,double b,double c)
{
if(a+b<=c||a+c<=b||c+b<=a)
{
System.out.print("两边之和必须大于第三边");
return ;
}
this.a=a;
this.b=b;
this.c=c;
}
//重写计算周长的方法
public double calPerimeter()
{
return a+b+c;
}
public String getType()
{
return "三角形";
}
public static void main (String[] args)
{
//如果不用抽象类的话,s1是不能够直接调用gettype方法的。
Shape s1=new Triangle("yello", 3, 4,5);
System.out.println(s1.getType());
System.out.print(s1.calPerimeter());
}
}
abstract与final不能同时出现:abstract类表示只能被继承,但是final类不能被继承。
abstract和static一般不能同时修饰方法:static修饰的方法表示属于类的,可以通过类来访问。但是如果同时也是abstract的话,则没有方法体。这就没办法调用。(内部类除外)
abstract和private不能同时修饰方法:private修饰的方法是不会被继承的。但是abstract需要继承。
6.5.2 抽象类的作用
作为子类的模版。
6.6 java8改进的接口
将抽象类“抽象”到极致,只包含抽象方法。就是接口。
6.6.1 接口的概念
接口定义的是多个类共同的公共行为规范,接口里通常是定义一组公共方法。
接口不提供任何实现,体现的是实现和规范相分离的设计哲学。
6.6.2 java8中接口的定义
关键词:interface
修饰符:public 或者省略,省略是默认default
一个接口可以继承多个接口
由于接口定义的是一种规范,因此接口没有构造器和初始化块,接口可以包含成员变量(静态常量),静态方法和抽象方法以及默认方法。都必须是public
- 静态常量:无论是否有修饰符,都是public static final的,需要在定义的时候指定默认值。可以跨包访问,但是因为是final,不能修改值。
- 接口里面的普通方法只能是public的抽象abstract方法
- 在接口定义默认方法,需要使用default修饰(默认都是public修饰,不能static修饰)
- 在接口定义类方法,需要使用static(默认都是public ,不能用default修饰)
- java里面最多定义一个public的接口,如果有public的接口,则主文件名和接口名相同
6.6.3 接口的继承
支持多继承,以逗号分格
6.6.4 使用接口
implements实现多个接口。如果一个类继承了一个接口,就必须把里面的抽象方法都实现,否则就必须定义成抽象类。
实现接口的方法时必须使用public
模拟多继承:接口名 引用变量名=new 类(初始化参数),类就可以访问接口的方法以及自己的方法。类的方法就变的很多。
package demo6;
//定义一个product接口
interface Product
{
int getProductTime();
}
public class Printer implements Product , Output{
private String[] printData=new String[MAX_CACHE_LINE];
//记录当前需打印的作业数
private int dataNum=0;
public void out()
{
//只要还有作业就继续打印
while(dataNum>0)
{
System.out.println(printData[0]);
System.arraycopy(printData, 1, printData, 0, --dataNum);
}
}
public void getData(String msg)
{
if(dataNum>=MAX_CACHE_LINE)
{
System.out.println("输出队列已经满了。添加失败");
}else {
printData[dataNum++]=msg;
}
}
public int getProductTime()
{
return 45;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建一个printer对象,当成output使用
Output o=new Printer();
o.getData("疯狂jav");
o.getData("疯狂jaba讲义");
o.out();
o.getData("疯狂安卓讲义");
o.getData("疯狂安卓");
o.out();
}
}
6.6.5 接口和抽象类
接口:体现一种规范,对于接口的实现者,接口定义了必须实现那些服务;对于接口的调用者,规定了调用者可以调用哪些方法。
抽象类:体现一种模版的设计,他是没有设计完的一个类,需要子类补充将它完成。
6.6.6 面向接口编程
- 简单的工厂模式
不太懂啊
package demo6;
public class Computer {
private Output out;
public Computer(Output out)
{
this.out=out;
}
//定义一个模拟获得字符串的方法
public void keyIn(String msg)
{
out.getData(msg);
}
public void print()
{
out.out();
}
}
package demo6;
public class OutputFactory {
public Output getOutput()
{
return new Printer();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
OutputFactory oF=new OutputFactory();
Computer c=new Computer(oF.getOutput());
c.keyIn("java");
c.keyIn("ilovayou");
c.print();
}
}
2.命令模式
定义一个接口,接口里面定义一个抽象的方法,作用在一个数组上。然后实例化这个接口,可以实例化多个,每个都是作用在数组上的一种方法,
???????????
6.7 内部类
定义在其他类内部的类叫做内部类。
包含内部类的类叫做外部类。
内部类的作用:
- 提供了更好的封装性,不允许同一个包中的其他类访问。
- 内部类可以直接访问外部类的私有数据。因为内部类可以当作外部类成员。外部类不可以访问内部类的实现细节
- 匿名内部类适合用于创建只需要一次使用的类。
内部类外部类区别: - 内部类比外部类多3个修饰符,private protected static
- 非静态内部类不能有静态成员
6.7.1 非静态内部类
在外部类里面使用非静态内部类时,和使用普通的类没有什么区别。非静态内部类可以访问外部类的pirvate成员,因为非静态内部类的对象里面,保存了一个外部类对象的引用。
外部类成员变量,内部类成员变量,内部类里面方法的局部变量可以同名,用this区分。
外部类不能够访问非晶态内部类的成员,必须创建一个对象才行。new inner(),,,因为外部类存在的时候,内部类不一定存在,但是内部类存在,外部类一定存在。
不允许在外部类的静态成员中直接使用非静态内部类
不允许在非静态内部类里面定义静态成员。
6.7.2 静态内部类
用static修饰的内部类叫做静态内部类。这个内部类属于外部类本身,不属于外部类的任何一个对象。
外部类不能够用statc修饰,因为外部类的上一级是包,所以没有类的概念,但是内部类的上一层是外部类,所以可以用static修饰。
静态内部类可以有静态成员和非静态成员,静态内部类不能够访问外部类的实例成员,只能访问类成员。(因为静态内部类里面只有外部类的引用,没有外部类对象的引用)
外部类依旧不能访问内部类的成员,但是可以通过类名或者对象访问内部类成员对象。
java允许定义接口内部类,默认是public static修饰。也就是说,接口的内部类一定是静态内部类。
6.7.3 使用内部类
在外部类内部使用内部类
基本上与平常使用普通类没有区别。唯一的区别是不要在外部类的静态成员中使用非静态内部类。
在外部类以外使用非静态内部类
在外部类以外的地方定义内部类变量的语法:
outclassname.innerclassname name;
创建非静态内部类对象(非静态内部类的构造器必须用外部类对象调用)
outerInstance.new InnerConstructor()
class Out
{
class In
{
public In(String msg)
{
System.out.println(msg);
}
}
}
public class CreateInnerInstance {
public static void main(String[] args) {
// TODO Auto-generated method stub
//使用outclass.innerclass的形式定义内部类变量
Out.In in;
//创建外部类对象
Out out=new Out();
//通过外部类对象创建内部类对象
in=out.new In("测试出错");
}
}
1.创建非静态内部类的子类
package demo6;
public class SubClass extends Out.In {
public SubClass(Out out)
{
out.super("hello");
}
}
- 在外部类以外使用静态内部类
new outclass.innerConstruction
可以看出无论是静态内部类还是非静态内部类,声明变量的方法都是一样的。区别在于创建内部类对象。优先考虑静态内部类。
6.7.4 局部内部类
放在方法里面的内部类
一般不用
6.7.5 java8改进的匿名内部类
匿名内部类适合创建只需要一次使用的类。创建匿名内部类时会立即创建一个该类的实例,这个类定义立即消失,匿名内部类不能重复使用。
匿名内部类必须继承一个父类,或者实现一个接口,但是最多只能是一个。
匿名内部类的两条规则:
- 不能是抽象类,因为抽象类不能被实例化,但是匿名内部类创建的时候就要创建对象
- 不能定义构造器,因为匿名内部类没有类名。
最常用的创建匿名内部类是需要创建某个接口类型的对象。
局部变量被匿名内部类访问,局部变量相当于自动加了final修饰。因此不能够再被修改。
6.8 java8新增的lambda表达式
6.8.1 lambda表达式入门
lambda表达式支持代码作为方法参数,可以创建只有一个抽象方法的接口的实例。
lambda表达式由形参列表 ->和方法体组成
6.8.2 lambda表达式和函数式接口
lambda表达式的目标类型必须是函数式接口
函数式接口代表只包含一个抽象方法的接口。函数式接口可以包含多个默认方法,类方法,但只能有一个抽象方法。
java8为函数式接口加了@FunctionalInterface注解 用于告诉编译器更严格的检查
6.8.3 方法引用和构造器引用
如果代码块只有一行代码,则可以在lambda表达式中使用方法引用和构造引用。
引用类方法 类名::类方法
引用特定对象的实力方法 特定对象::实例方法
引用某类对象的实例方法 类名::实例方法
引用构造器 类名::new
6.8.4 lambda表达式和匿名内部类的联系和区别
lambda表达式是匿名内部类的一种简化。
相同点:
都可以直接访问“effectively final”的局部变量,以及外部类的成员变量
所创建的对象可以直接调用从接口中继承的默认方法
区别
匿名内部类可以为任意接口创建实例,而lambda表达式必须是函数式接口
匿名内部类可以为抽象类甚至普通类创建实例。
匿名内部类的方法体可以调用接口中定义的默认方法,但是lambda不可以,它只有对象可以调用。
6.9 枚举类
枚举类是指实例有限而且固定的类
6.9.2 枚举类入门
枚举类是一种特殊的类,可以有自己的成员变量,方法,可以实现一个或者多个接口。
- 默认继承java.lang.Enum类,不能显式继承其他父类。
- 使用Enum定义,非抽象的枚举类默认是final修饰,不能派生子类
- 构造器为private
- 所有实例必须在第一行显式列出,系统默认加上public static final
- 如果需要使用某个实例,用EnumClass.variable
6.9.3 枚举类的成员变量,方法和构造器
枚举类的成员变量最好都使用private final修饰
如果构造函数有参数,则在第一行列出实例的时候,要写上参数。
枚举类的实例只能是枚举值,不能随意通过new来创建。???
6.9.4 实现接口的枚举类
与普通类完全一样,也需要实现该接口所包含的方法
如果不同的枚举值想在调用一个方法时呈现不同的行为方式,则可以让每个枚举值分别实现该方法,这个时候,不是在创建枚举类的实例,而是创建匿名子类的实例。
6.9.5 包含抽象方法的枚举类
每个枚举值都必须为抽象方法提供实现,否则报错。
6.10 对象和垃圾回收
6.10.1 对象在内存中的状态
可达状态
可恢复状态
不可达状态
6.10.2 强制垃圾回收
System.gc()
Runtime.getRuntime().gc()