疯狂java第六章——面向对象(下)

一、 java8增强的包装类

  包装类的作用:Java语言中,一切都是对象,但是有例外:8个基本数据类型不是对象,因此在很多时候非常不方便。

       为此,Java提供为8个基本类型提供了对应的包装类:

             byte           Byte

             short          Short

             int            Integer

             long           Long

             char           Character

             float          Float

             double         Double

             boolean        Boolean

自动装箱: 当我们把一个基本类型的值(20),赋值给引用变量时候,系统可以自动将它“包装”为相应的包装类的实例

                    程序需要对象时,如果给的只是一个基本类型的值,系统会将它自动装箱为包装类的实例

                    达到的效果:有了自动装箱之后,基本类型的值可以当成对象用——其实是【假相】。

  自动拆箱: 当我们需要一个基本类型的值时,但实际上传入的包装类的对象。系统会自动把对象“剥”开,得到它的值。

                    达到的效果:有了自动拆箱之后,包装类的对象可当成基本类型的值用——其实是【假相】。

   包装类额外功能:

          将字符串,转换相应的基本类型的值。

          每个包装类,都提供了static修饰的parseXxx(String)用于将字符串转为相应的基本类型的值。                            valueOf(String)

          所有基本类型的值,都可通过 + "" 转换为String。

String intStr=5+"";

public class Primitive2String
{
	public static void main(String[] args)
	{
		String intStr = "123";
		// 把一个特定字符串转换成int变量
		int it1 = Integer.parseInt(intStr);
		int it2 = Integer.valueOf(intStr);
		System.out.println(it2);
		String floatStr = "4.56";
		// 把一个特定字符串转换成float变量
		float ft1 = Float.parseFloat(floatStr);
		float ft2 = Float.valueOf(floatStr);
		System.out.println(ft2);
		// 把一个float变量转换成String变量
		String ftStr = String.valueOf(2.345f);
		System.out.println(ftStr);
		// 把一个double变量转换成String变量
		String dbStr = String.valueOf(3.344);
		System.out.println(dbStr);
		// 把一个boolean变量转换成String变量
		String boolStr = String.valueOf(true);
		System.out.println(boolStr.toUpperCase());
	}
}

基本类型和字符串之间的转换方法

  1. 字符串转换成基本类型
    int i=Integer.parseInt(str);
    或者
    int i=new Integer(str);
  2. 基本类型转换成字符串
    String flStr=String.valueOf(float变量);
    或者
    String flStr=float变量+“”;

包装类比较大小

  1. 如果直接给integer数字,如果数字在-128-127之间,则两个数字相等,包装类相等。
  2. 如果用new integer(2)赋值,则必须两个包装类指向同一个对象才相等。
  3. 有一个compare(a,b)可以直接用来比较包装类大小.
    a>b 返回1
    a=b 返回0
    a<b 返回-1

二、处理对象

打印对象和toString方法

toString方法

System.out.println(p);

System.out.println(p.toString());两者效果一样
toString () 是Object类里面的一个实例方法,所有对象都具有这个方法。
系统自带的tostring“类名+@+hashcode”
可以自己重写这个方法。

==和equals方法

“==”

  1. 对于基本类型的数值类型(包括char),只要两个变量的值相等,返回true。
  2. 对于引用类型,必须指向同一个对象,才返回true。
    注意:“==“不能用于比较没有父子关系的两个对象。
    ”hello“直接量和new String (“hello”)区别
    直接使用hello这种可以在编译时候计算出来的字符串值,jvm会用常量池来管理。jvm会保证相同的字符串直接量只有一个,不会产生多个副本。
    第二种,jvm会先用常量池来管理直接量,再调用string构造器来创建 i 个新的string对象。新创建的对象保存在堆中

equals()

         Java首先提供了==来判断两个引用变量是否相等。

            == 要求两个引用变量必须指向同一个对象才算相等。

         但equals()就不同了,也用于判断两个对象是否相等。

            默认情况下,你调用的equals()方法是Object提供了,Object所提供的equals()方法的判断标准,与==的判断标准是完全一样。

           可以这样说, Object提供的equals()几乎不能直接用于判断两个对象是否相等。

         如果你要根据自己的标准来判断两个对象是否相等,就需要重写equals()方法。

       Java在提供了String类,已经重写equals()方法,

        标准是:只要两个字符串包含相同的字符序列,这两个字符串通过equals()比较就会返回true.

       因此:如果以后我们要判断两个String所包含的字符序列是否相等,应该通过a.equals(b)进行比较。      

          

三、 类成员

 理解类成员

类成员就是用static修饰的成员变量,方法,初始化块,内部类的统称。能通过对象访问这些类成员。即使对象是null 。类成员不能访问实例成员,因为类成员的作用域更大,可能类成员还存在,但是成员变量已经不存在了,所以不能够访问实例成员。

单例类

           一个类始终只能创建一个实例,称为单例类。
         如果不想让别的类轻易的创建该类的对象,就需要把类的构造函数设置成private,但是这个时候就需要有一个方法来创建一个对象,由于使用这个方法的时候还没有对象,因此需要给方法是public static的。
同时。为了满足只能创建一个对象,则必须把已经创建的对象保存起来,每次创建对象之前都检查一下是否只有一个对象。由于  存储对象的成员变量需要通过上面的static方法调用,因此需要设置为static。

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

保证只产生一个实例,两次产生的Singleton对象实际是同一个对象

四、 final修饰符

 final可修饰类、方法、变量(一切变量, 类变量、实例变量、局部变量)。

      final与abstract是互斥,永远不可能同时出现

      当final修饰变量时,总则是:该变量被赋值之后,再也不能重新赋值

final成员变量

类变量:必须指定初始化,在静态初始化块或者声明该变量时初始化
实例变量:在普通初始化块,或者声明该变量时候,或者构造器。如果已经在普通初始化块赋值,则不可以再在构造器中赋值。
         注意⚠:普通方法不能访问final修饰的成员变量。
final修饰的成员变量必须程序员自己显示初始化,系统不会默认赋值。

final局部变量

因为系统不会给局部变量初始化,因此在用final修饰的局部变量不一定非由程序员显示初始化。
不能对final修饰的形参赋值

 final修饰基本类型变量和引用类型变量的区别

final修饰基本类型变量,就不能更改值了。

如果是引用类型,引用类型变量不能被重新赋值,但可以改变引用类型变量所引用对象的内容

class Person
{
	private int age;
	public Person(){}
	// 有参数的构造器
	public Person(int age)
	{
		this.age = age;
	}
	// 省略age的setter和getter方法
	// age的setter和getter方法
	public void setAge(int age)
	{
		this.age = age;
	}
	public int getAge()
	{
		return this.age;
	}
}
public class FinalReferenceTest
{
	public static void main(String[] args)
	{
		// final修饰数组变量,iArr是一个引用变量
		final int[] iArr = {5, 6, 12, 9};
		System.out.println(Arrays.toString(iArr));
		// 对数组元素进行排序,合法
		Arrays.sort(iArr);
		System.out.println(Arrays.toString(iArr));
		// 对数组元素赋值,合法
		iArr[2] = -8;
		System.out.println(Arrays.toString(iArr));
		// 下面语句对iArr重新赋值,非法
		// iArr = null;
		// final修饰Person变量,p是一个引用变量
		final Person p = new Person(45);
		// 改变Person对象的age实例变量,合法
		p.setAge(23);
		System.out.println(p.getAge());
		// 下面语句对p重新赋值,非法
		// p = null;
	}
}

可执行“宏替换”的final变量

变量满足三个条件,则变量相当于一个直接量

(1)final修饰

(2)在定义的时候指定了初始值(只有在定义的时候赋值才有这种效果)

(3)值可以在编译的时候确定下来

 String类:      

        只要程序中用到了任何“字符串直接量”,该直接量就会被放入一个“房间”(池、Pool),下次如果再用到该直接量时,就直接从池中取出来用。

 

 final 方法

final方法不能够被重写。如果父类的方法不希望子类重写,只要加上final 就好。
但是父类的private 是不会被子类继承的,因此也不会有重写这个说法。因此如果父类的private方法被final了,子类仍然可以写一个一样的方法相同方法名,相同形参列表,相同返回值。

public class PrivateFinalMethodTest
{
	private final void test(){}
}
class Sub extends PrivateFinalMethodTest
{
	// 下面方法定义将不会出现问题
	public void test(){}
}

final类

不能有子类的类,不可被 继承

不可变类

不可变类是创建该类的实例后,实例变量不能够更改。
创建自定义的不可变类方法:
1,类的成员变量用private和final修饰
2,提供带参数的构造函数,用于根据传入参数来初始化类的成员变量
3,该类仅有getter
4,重新定义equals()和hashcode()

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;
	}
	// 仅为两个实例变量提供getter方法
	public String getDetail()
	{
		return this.detail;
	}
	public String getPostCode()
	{
		return this.postCode;
	}
	//重写equals()方法,判断两个对象是否相等。
	public boolean equals(Object obj)
	{
		if (this == obj)
		{
			return true;
		}
		if(obj != null && obj.getClass() == Address.class)
		{
			Address ad = (Address)obj;
			// 当detail和postCode相等时,可认为两个Address对象相等。
			if (this.getDetail().equals(ad.getDetail())
				&& this.getPostCode().equals(ad.getPostCode()))
			{
				return true;
			}
		}
		return false;
	}
	public int hashCode()
	{
		return detail.hashCode() + postCode.hashCode() * 31;
	}
}

当创建不可变类的时候,如果成员变量里面有引用类型,则很可能创建出一个可变类,因为成员变量的内容可以更改。必须采用一些其他的方法,才能创建真正的不可变类。

 缓存实例的不可变类

缓存实例的不可变类,是因为有的时候一个对象的某个成员被多次引用,为了节省开销,可以把它缓存起来。下边是把它缓存在数组里面,如果缓存里面已经有了,就直接返回实例,如果没有,就新建实例加进去。

class CacheImmutale
{
	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 CacheImmutale(String name)
	{
		this.name = name;
	}
	public String getName()
	{
		return name;
	}
	public static CacheImmutale valueOf(String name)
	{
		// 遍历已缓存的对象,
		for (int i = 0 ; i < MAX_SIZE; i++)
		{
			// 如果已有相同实例,直接返回该缓存的实例
			if (cache[i] != null
				&& cache[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)
		{
			CacheImmutale 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)
	{
		CacheImmutale c1 = CacheImmutale.valueOf("hello");
		CacheImmutale c2 = CacheImmutale.valueOf("hello");
		// 下面代码将输出true
		System.out.println(c1 == c2);
	}
}

五、抽象类

抽象方法是只有方法的签名,没有方法的实现

抽象方法和抽象类

抽象方法和抽象类必须使用abstract修饰,有抽象方法的类一定是抽象类。
规则:

  1. 抽象方法不能有方法体
  2. 抽象类不能够被实例化,无法使用new关键字来调用抽象类的构造器创建抽象类的实例
  3. 抽象类的构造函数不能够用来创造实例,主要用于被子类调用
  4. 含有抽象方法的类(包括直接定义了一个抽象方法;继承了一个抽象父类,但是没有完全实例化父类包含的抽象类;或是实现了一个接口,但是没有完全实例化接口包含的抽象方法)只能被定义成抽象类
public abstract class Shape
{
	{
		System.out.println("执行Shape的初始化块...");
	}
	private String color;
	// 定义一个计算周长的抽象方法
	public abstract double calPerimeter();
	// 定义一个返回形状的抽象方法
	public abstract String getType();
	// 定义Shape的构造器,该构造器并不是用于创建Shape对象,
	// 而是用于被子类调用
	public Shape(){}
	public Shape(String color)
	{
		System.out.println("执行Shape的构造器...");
		this.color = color;
	}
	// 省略color的setter和getter方法
	public void setColor(String color)
	{
		this.color = color;
	}
	public String getColor()
	{
		return this.color;
	}
}

抽象类不能用于创建实例,只能当作父类被其他子类调用

 

abstract与final不能同时出现:abstract类表示只能被继承,但是final类不能被继承。
abstract和static一般不能同时修饰方法:static修饰的方法表示属于类的,可以通过类来访问。但是如果同时也是abstract的话,则没有方法体。这就没办法调用。(但可以同时修饰内部类)
abstract和private不能同时修饰方法:private修饰的方法是不会被继承的。但是abstract需要继承。

抽象类的作用

正是用于被子类继承,作为模板模式设计,作为子类通用模板

六、 java8改进的接口

将抽象类“抽象”到极致,只包含抽象方法。就是接口。

 接口的概念

接口定义的是多个类共同的公共行为规范,接口里通常是定义一组公共方法。
接口不提供任何实现,体现的是实现和规范相分离的设计哲学。

 java8中接口的定义

  [修饰符] interface 接口名 extends 父接口1,父接口2...
        {
             0~N个field —— 只能是public static final修饰的field。
             0~N个方法  —— 只能是public abstract方法。
             0~N个内部类|接口|枚举   —— 只能是public static修饰。
        }


修饰符:public 或者省略,省略是默认default
  接口名一般用形容词。接口名与类名的命名规范是相同:每个单词的首字母大写。

由于接口定义的是一种规范,因此接口没有构造器和初始化块,接口可以包含成员变量(静态常量),静态方法和抽象方法以及默认方法。都必须是public

  1. 静态常量:无论是否有修饰符,都是public static final的,需要在定义的时候指定默认值。可以跨包访问,但是因为是final,不能修改值。
  2. 接口里面的普通方法只能是public的抽象abstract方法
  3. 在接口定义默认方法,需要使用default修饰(默认都是public修饰,不能static修饰)
  4. 在接口定义类方法,需要使用static(默认都是public ,不能用default修饰)
  5. java里面最多定义一个public的接口,如果有public的接口,则主文件名和接口名相同
  6. 
    package lee;
    
    /**
     * Description:
     * 网站: <a href="http://www.crazyit.org">疯狂Java联盟</a><br>
     * Copyright (C), 2001-2018, Yeeku.H.Lee<br>
     * This program is protected by copyright laws.<br>
     * Program Name:<br>
     * Date:<br>
     * @author Yeeku.H.Lee kongyeeku@163.com
     * @version 1.0
     */
    public interface Output
    {
    	// 接口里定义的成员变量只能是常量
    	int MAX_CACHE_LINE = 50;
    	// 接口里定义的普通方法只能是public的抽象方法
    	void out();
    	void getData(String msg);
    	// 在接口中定义默认方法,需要使用default修饰
    	default void print(String... msgs)
    	{
    		for (String msg : msgs)
    		{
    			System.out.println(msg);
    		}
    	}
    	// 在接口中定义默认方法,需要使用default修饰
    	default void test()
    	{
    		System.out.println("默认的test()方法");
    	}
    	// 在接口中定义类方法,需要使用static修饰
    	static String staticTest()
    	{
    		return "接口里的类方法";
    	}
    	// 定义私有方法
    	private void foo()
    	{
    		System.out.println("foo私有方法");
    	}
    	// 定义私有静态方法
    	private static void bar()
    	{
    		System.out.println("bar私有静态方法");
    	}
    }
    

     

接口的继承

支持多继承,一个接口可以有多个直接父接口,以逗号分格 

使用接口

一个类在继承另一个类的同时,还可以实现多个接口。
 

[修饰符] class 类名 extends 父类 implements 接口1,接口2.....
{
    类体部分
}

接口用途:

1.定义变量,也可用于强制类型转换

2.调用接口中定义的常量  

3.被其它类实现

implements实现多个接口。如果一个类继承了一个接口,就必须把里面的抽象方法都实现,否则就必须定义成抽象类。
实现接口的方法时必须使用public
模拟多继承:接口名 引用变量名=new 类(初始化参数),类就可以访问接口的方法以及自己的方法。类的方法就变的很多。


import lee.Output;

// 定义一个Product接口
interface Product
{
	int getProduceTime();
}
// 让Printer类实现Output和Product接口
public class Printer implements Output , Product
{
	private String[] printData
		= new String[MAX_CACHE_LINE];
	// 用以记录当前需打印的作业数
	private int dataNum = 0;
	public void out()
	{
		// 只要还有作业,继续打印
		while(dataNum > 0)
		{
			System.out.println("打印机打印:" + printData[0]);
			// 把作业队列整体前移一位,并将剩下的作业数减1
			System.arraycopy(printData , 1
				, printData, 0, --dataNum);
		}
	}
	public void getData(String msg)
	{
		if (dataNum >= MAX_CACHE_LINE)
		{
			System.out.println("输出队列已满,添加失败");
		}
		else
		{
			// 把打印数据添加到队列里,已保存数据的数量加1。
			printData[dataNum++] = msg;
		}
	}
	public int getProduceTime()
	{
		return 45;
	}
	public static void main(String[] args)
	{
		// 创建一个Printer对象,当成Output使用
		Output o = new Printer();
		o.getData("轻量级Java EE企业应用实战");
		o.getData("疯狂Java讲义");
		o.out();
		o.getData("疯狂Android讲义");
		o.getData("疯狂Ajax讲义");
		o.out();
		// 调用Output接口中定义的默认方法
		o.print("孙悟空" , "猪八戒" , "白骨精");
		o.test();
		// 创建一个Printer对象,当成Product使用
		Product p = new Printer();
		System.out.println(p.getProduceTime());
		// 所有接口类型的引用变量都可直接赋给Object类型的变量
		Object obj = p;
	}
}

Printer类实现了Output接口和Product接口,因此Printer对象既可直接赋给Output变量,也可直接赋给Prouduct变量,

接口和抽象类

接口:体现一种规范,对于接口的实现者,接口定义了必须实现那些服务;对于接口的调用者,规定了调用者可以调用哪些方法。
抽象类:体现一种模版的设计,他是没有设计完的一个类,需要子类补充将它完成。

 

除此之外,接口和抽象类在用法上也存在如下差别。
➢接口里只能包含抽象方法、静态方法、默认方法和私有方法,不能为普通方法提供方法实现;抽象类则完全可以包含普通方法。
➢接口里只能定义静态常量,不能定义普通成员变量;抽象类里则既可以定义普通成员变量,也可以定义静态常量。
➢接口里不包含构造器;抽象类里可以包含构造器,抽象类里的构造器并不是用于创建对象,而是让其子类调用这些构造器来完成属于抽象类的初始化操作。

➢接口里不能包含初始化块:但抽象类则完全可以包含初始化块。
➢一个类最多只能有一个直接父类,包括抽象类;但一个类可以直接实现多个接口,通过实现多个接口可以弥补Java单继承的不足。”
 

面向接口编程

  1. 简单的工厂模式
    让Computer类组合一个Output类型的对象,将Computer类与Printer类分离,便于系统重构,

import lee.Output;

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

 

2.命令模式

定义一个接口,接口里面定义一个抽象的方法,作用在一个数组上。然后实例化这个接口,可以实例化多个,每个都是作用在数组上的一种方法,
???????????

七、内部类

定义在其他类内部的类叫做内部类。
包含内部类的类叫做外部类。
内部类的作用:

  1. 提供了更好的封装性,不允许同一个包中的其他类访问。
  2. 内部类可以直接访问外部类的私有数据。因为内部类可以当作外部类成员。外部类不可以访问内部类的实现细节
  3. 匿名内部类适合用于创建只需要一次使用的类。
    内部类外部类区别:
  4. 内部类比外部类多3个修饰符,private protected static
  5. 非静态内部类不能有静态成员

 非静态内部类

在外部类里面使用非静态内部类时,和使用普通的类没有什么区别。

非静态内部类可以访问外部类的pirvate成员,因为非静态内部类的对象里面,保存了一个外部类对象的引用。
外部类成员变量,内部类成员变量,内部类里面方法的局部变量可以同名,用this区分。
外部类不能够访问非晶态内部类的成员,必须创建一个对象来调用访问实例成员才行。new inner(),,,因为外部类存在的时候,内部类不一定存在,但是内部类存在,外部类一定存在。
不允许在外部类的静态成员中直接使用非静态内部类  

 

public class Outer
{
	private int outProp = 9;
	class Inner
	{
		private int inProp = 5;
		public void acessOuterProp()
		{
			// 非静态内部类可以直接访问外部类的private成员变量
			System.out.println("外部类的outProp值:"
				+ outProp);
		}
	}
	public void accessInnerProp()
	{
		// 外部类不能直接访问非静态内部类的实例变量,
		// 下面代码出现编译错误
		// System.out.println("内部类的inProp值:" + inProp);
		// 如需访问内部类的实例变量,必须显式创建内部类对象
		System.out.println("内部类的inProp值:"
			+ new Inner().inProp);
	}
	public static void main(String[] args)
	{
		// 执行下面代码,只创建了外部类对象,还未创建内部类对象
		Outer out = new Outer();      //①
		out.accessInnerProp();
	}
}

静态内部类

用static修饰的内部类叫做静态内部类。这个内部类属于外部类本身,不属于外部类的任何一个对象。
外部类不能够用statc修饰,因为外部类的上一级是包,所以没有类的概念,但是内部类的上一层是外部类,所以可以用static修饰。
静态内部类可以有静态成员和非静态成员,静态内部类不能够访问外部类的实例成员,只能访问类成员。(因为静态内部类里面只有外部类的引用,没有外部类对象的引用)
外部类依旧不能访问内部类的成员,但是可以通过类名或者对象访问内部类成员对象。java允许定义接口内部类,默认是public static修饰。也就是说,接口的内部类一定是静态内部类。

public class AccessStaticInnerClass
{
	static class StaticInnerClass
	{
		private static int prop1 = 5;
		private int prop2 = 9;
	}
	public void accessInnerProp()
	{
		// System.out.println(prop1);
		// 上面代码出现错误,应改为如下形式:
		// 通过类名访问静态内部类的类成员
		System.out.println(StaticInnerClass.prop1);
		// System.out.println(prop2);
		// 上面代码出现错误,应改为如下形式:
		// 通过实例访问静态内部类的实例成员
		System.out.println(new StaticInnerClass().prop2);
	}
}

 使用内部类

1.在外部类内部使用内部类
基本上与平常使用普通类没有区别。唯一的区别是不要在外部类的静态成员中使用非静态内部类。

2.在外部类以外使用非静态内部类
在外部类以外的地方定义内部类变量的语法:
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)
	{
		Out.In in = new Out().new In("测试信息");
		/*
		上面代码可改为如下三行代码:
		使用OutterClass.InnerClass的形式定义内部类变量
		Out.In in;
		创建外部类实例,非静态内部类实例将寄存在该实例中
		Out out = new Out();
		通过外部类实例和new来调用内部类构造器创建非静态内部类实例
		in = out.new In("测试信息");
		*/
	}
}

1.创建非静态内部类的子类

package demo6;

public class SubClass extends Out.In {
    public SubClass(Out out)
    {
        out.super("hello");
    }

}
  1. 在外部类以外使用静态内部类
    new outclass.innerConstruction

可以看出无论是静态内部类还是非静态内部类,声明变量的方法都是一样的。区别在于创建内部类对象。优先考虑静态内部类

局部内部类

放在方法里面的内部类
一般不用

 java8改进的匿名内部类

   new 父类构造器(实参列表) | 实现接口()
       {
            // 匿名内部类的 类体部分
       };

匿名内部类适合创建只需要一次使用的类。创建匿名内部类时会立即创建一个该类的实例,这个类定义立即消失,匿名内部类不能重复使用。
匿名内部类必须继承一个父类,或者实现一个接口,但是最多只能是一个。
匿名内部类的两条规则:

  1. 不能是抽象类,因为抽象类不能被实例化,但是匿名内部类创建的时候就要创建对象
  2. 不能定义构造器,因为匿名内部类没有类名。
    最常用的创建匿名内部类是需要创建某个接口类型的对象。
    局部变量被匿名内部类访问,局部变量相当于自动加了final修饰。因此不能够再被修改。
    interface Product
    {
    	public double getPrice();
    	public String getName();
    }
    public class AnonymousTest
    {
    	public void test(Product p)
    	{
    		System.out.println("购买了一个" + p.getName()
    			+ ",花掉了" + p.getPrice());
    	}
    	public static void main(String[] args)
    	{
    		AnonymousTest ta = new AnonymousTest();
    		// 调用test()方法时,需要传入一个Product参数,
    		// 此处传入其匿名实现类的实例
    		ta.test(new Product()
    		{
    			public double getPrice()
    			{
    				return 567.8;
    			}
    			public String getName()
    			{
    				return "AGP显卡";
    			}
    		});
    	}
    }
    

     

八、java8新增的lambda表达式

  lambda表达式入门

public class CommandTest2
{
	public static void main(String[] args)
	{
		ProcessArray pa = new ProcessArray();
		int[] array = {3, -4, 6, 4};
		// 处理数组,具体处理行为取决于匿名内部类
		pa.process(array , (int[] target)->{
				int sum = 0;
				for (int tmp : target )
				{
					sum += tmp;
				}
				System.out.println("数组元素的总和是:" + sum);
			});
	}
}


 

从上面程序中的粗体字代码可以看出,这段粗体字代码与创建匿名内部类时需要实现的pocssintarget()方法完全相同,只是不需要new XxxO{} 这种烦琐的代码,不需要指出重写的方法名字,也不需要给出重写的方法的返回值类型一只 要给出重写的方法括号以及括号里的形参列表即可。
一从上面介绍可以看出,当使用Lambda表达式代替匿名内部类创建对象时,Lambda 表达式的代码块将会代替实现抽象方法的方法体,Lambda表达式就相当一-个匿名方法。
从上面语法格式可以看出,Lambda 表达式的主要作用就是代替匿名内部类的烦琐语法。它由三部分组成。
➢形参列表。形参列表允许省略形参类型。如果形参列表中只有一个参数,甚至连形参列表的圆
括号也可以省略。

➢箭头(->)。 必须通过英文中画线和大于符号组成。
➢代码块。如果代码块只包含一条语句,Lambdla表达式允许省略代码块的花括号,那么这条语向就不要用花括号表示语句结束。Lambda代码块只有一条return语句,甚至可以省略return关键字。Lambda表达式需要返回值,而它的代码块中仅有一条省略了return的语句,Lambda 表达式自动返回这条语句的值

 lambda表达式和函数式接口

interface FkTest
{
	void run();
}

public class LambdaTest
{
	public static void main(String[] args)
	{
		// Runnable接口中只包含一个无参数的方法
		// Lambda表达式代表的匿名方法实现了Runnable接口中唯一的、无参数的方法
		// 因此下面的Lambda表达式创建了一个Runnable对象
		Runnable r = () -> {
			for(int i = 0 ; i < 100 ; i ++)
			{
				System.out.println();
			}
		};
//		// 下面代码报错: 不兼容的类型: Object不是函数接口
//		Object obj = () -> {
//			for(int i = 0 ; i < 100 ; i ++)
//			{
//				System.out.println();
//			}
//		};
         //下面对表达式强制转换,确定表达式的目标类型为Runnable

		Object obj1 = (Runnable)() -> {
			for(int i = 0 ; i < 100 ; i ++)
			{
				System.out.println();
			}
		};

		// 同样的Lambda表达式可以被当成不同的目标类型,唯一的要求是:
		// Lambda表达式的形参列表与函数式接口中唯一的抽象方法的形参列表相同
		Object obj2 = (FkTest)() -> {
			for(int i = 0 ; i < 100 ; i ++)
			{
				System.out.println();
			}
		};

	}
}

lambda表达式的目标类型必须是函数式接口
函数式接口代表只包含一个抽象方法的接口。函数式接口可以包含多个默认方法,类方法,但只能有一个抽象方法。
java8为函数式接口加了@FunctionalInterface注解 用于告诉编译器更严格的检查

 方法引用和构造器引用

如果代码块只有一行代码,则可以在lambda表达式中使用方法引用和构造引用。
引用类方法 类名::类方法
引用特定对象的实力方法 特定对象::实例方法
引用某类对象的实例方法 类名::实例方法
引用构造器 类名::new

 lambda表达式和匿名内部类的联系和区别

lambda表达式是匿名内部类的一种简化。
相同点:
都可以直接访问“effectively final”的局部变量,以及外部类的成员变量
所创建的对象可以直接调用从接口中继承的默认方法
区别
匿名内部类可以为任意接口创建实例,而lambda表达式必须是函数式接口
匿名内部类可以为抽象类甚至普通类创建实例。
匿名内部类的方法体可以调用接口中定义的默认方法,但是lambda不可以,它只有对象可以调用。

九、枚举类

枚举类是指实例有限而且固定的类

枚举类入门

枚举类是一种特殊的类,可以有自己的成员变量,方法,可以实现一个或者多个接口。

1.默认继承java.lang.Enum类,不能显式继承其他父类。

2.使用Enum定义,非抽象的枚举类默认是final修饰,不能派生子类

3.构造器为private

4.所有实例必须在第一行显式列出,系统默认加上public static final

如果需要使用某个实例,用EnumClass.variable

 

所有的枚举类都继承了java lng Emm类,所以枚举类可以直核使用java. lang Enum类中所包含的方法。java lang Enum类中提供了如下几个方法。
➢int compareTo(E o):该方法用于与指定枚举对象比较顺序,同一个枚举实例只能与相同类型的枚举实例进行比较。如果该枚举对象位于指定枚举对象之后,则返回正整数:如果该枚举对象位于指定枚举对象之前,则返回负整数,否则返回零。
➢String name():返回此枚举实例的名称,这个名称就是定义枚举类时列出的所有枚举值之一。与此方法相比,大多数程序员应该优先考虑使用tString()方法, 因为toSring()方法返 回更加用户友好的名称。
➢intordinal():返回枚举值在枚举类中的索引值(就是枚举值在枚举声明中的位置,第一个枚举值的索引值为零)。
➢String toString():返回枚举常量的名称,与name方法相似,但toString方法更常用。
➢public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name):这是一个静态方法,用于返回指定枚举类中指定名称的枚举值。名称必须与在该枚举类中声明枚举值时所用的标识符完全匹配,不允许使用额外的空白字符。
正如前面看到的,当程序使用Sytemoutprintn(s)语句来打印枚举值时,实际上输出的是该枚举值的toString0方法, 也就是输出该枚举值的名字。
 

 枚举类的成员变量,方法和构造器

枚举类的成员变量最好都使用private final修饰
如果构造函数有参数,则在第一行列出实例的时候,要写上参数。
枚举类的实例只能是枚举值,不能随意通过new来创建。???

6.9.4 实现接口的枚举类

与普通类完全一样,也需要实现该接口所包含的方法
如果不同的枚举值想在调用一个方法时呈现不同的行为方式,则可以让每个枚举值分别实现该方法,这个时候,不是在创建枚举类的实例,而是创建匿名子类的实例。

6.9.5 包含抽象方法的枚举类

每个枚举值都必须为抽象方法提供实现,否则报错。

6.10 对象和垃圾回收

6.10.1 对象在内存中的状态

可达状态
可恢复状态
不可达状态

6.10.2 强制垃圾回收

System.gc()
Runtime.getRuntime().gc()

6.10.3 finalize方法

6.11 修饰符的适用范围

6.12 使用JAR文件



 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值