java核心技术 第11版 接口,Iambda表达式, 内部类

接口(interface)又来描述类应该做什么, 而不指定如何做
一个类可以实现(implement)一个或多个接口。
lambda表达式可以用一种精巧的方式表示使用回调或可变行为的代码。
内部类(inner class)定义在另一个类的内部, 其方法可以访问包含它们的外部类的字段
代理(proxy)可以用来构造系统级的工具。

接口

接口的概念

接口是对希望符合这个接口的类的一组需求
让类实现接口的步骤:

  1. 将类声明为实现给定的接口, 使用关键字implements
  2. 对接口中的所有方法提供定义

java是一种强类型语言, 调用方法时编译器要能检查这个方法确实存在

interfaces/EmployeeSortTest.java

package interfaces;

import java.util.*;

/**
 * This program demonstrates the use of the Comparable interface.
 * @author Cay Horstmann
 */

public class EmployeeSortTest 
{
    public static void main(String[] args)
    {
        var staff = new Employee[3];
        staff[0] = new Employee("Harry Hacker", 35000);
        staff[1] = new Employee("Carl Cracker", 75000);
        staff[2] = new Employee("Tony Tester", 38000);

        Arrays.sort(staff);

        //print out information about all Employee objects
        for (Employee e: staff)
            System.out.println("name=" + e.getName() + ", salary=" + e.getSalary());
    }
}

interfaces/Employee.java

package interfaces;

public class Employee implements Comparable<Employee> 
{
    private String name;
    private double salary;

    public Employee(String name, double salary)
    {
        this.name = name;
        this.salary = salary;
    }
    public String getName()
    {
        return name;
    }

    public double getSalary()
    {
        return salary;
    }
    public void raiseSalary(double byPercent)
    {
        double raise = salary * byPercent / 100;
        salary += raise;
    }
    /**
     * compare employees by salary
     * @param other another Employee object
     * @return a negative value if this employee has a lower salary than
     * otherObject, 0 if the salaries are the same, a positive value otherwise 
     */
    public int compareTo(Employee other)
    {
        return Double.compare(salary, other.salary);
    }
}

API

java.lang.Comparable< T >
int compareTo​(T o)
Compares this object with the specified object for order.
java.util.Arrays
static void sort​(Object[] a)
Sorts the specified array of objects into ascending order, according to the natural ordering of its elements.
java.lang.Integer
static int compare​(int x, int y)
Compares two int values numerically.
java.lang.Double
static int compare​(double d1, double d2)
Compares the two specified double values.

接口的属性

接口不是类, 但可以声明接口的变量

Comparable x;

j接口变量必须引用实现了接口的类对象

x= new Employee(... ...);

可以使用instanceof检查对象是否实现该接口

if (anObject instanceof Comparable)
{
	...
}

接口中的字段总是public static final
每个类只能有一个超类, 但可以实现多个接口

接口与抽象类

java的设计者选择不支持多重继承
接口可以提供继承的大多数好处, 且能避免多重继承的复杂性和低效性。

静态和私有方法

java8中允许在接口中增加静态方法, 通常的方法都是将静态方法放在伴随类中
然而, 在实现接口时, 没有必要再为实用工具另外提供一个伴随类
java9中, 接口中的方法可以是private

默认方法

可以为接口提供一个默认实现, 使用default标记

public interface Comparable< T >
{
	default int CompareTo(T other)
	{return 0;}			//all elements are the same.
}

默认方法可以调用其他方法

public interface Collection
{
	int size();
	default boolean isEmpty()  { return size() == 0;}
	...
}

默认方法的一个重要作用是“接口演化”(interface evolution

为借口增加一个默认方法可以保证** 源代码兼容(source compatible)**
且可以保证** 二进制兼容 **

解决默认方法冲突

java规则如下:

  1. 超类优先
  2. 接口冲突, 如果两个接口都提供了相同的默认方法, 必须覆盖这个方法来解决冲突
interface Person
{
	default String getName() {return "";}
}
interface Named
{
	default String getName() {return getClass().getName() + "_" + hashCode();}
}
class Student implements Person, Named
{
	public String getName( return Person.super.getName())
}

java的类优先规则确保了超类和接口之间发生方法冲突时, 优先方法.

接口与回调

回调(callback) 可以指定某个特定事件发生是应该采取的动作

package timer;
/**
 * @author Cay Horstmann
 */

 import java.awt.*;
 import java.awt.event.*;
 import java.time.*;
 import javax.swing.*;

public class TimerTest 
{
    public static void main(String[] args)
    {
        var listener = new TimePrinter();
        //construct a timer that calls the listener
        //once every second
        var timer = new Timer(1000, listener);
        timer.start();

        //keep program running until the user select"OK"
        JOptionPane.showMessageDialog(null, "Quit program?");
        System.exit(0);

    }
}

class TimePrinter implements ActionListener
{
    public void actionPerformed(ActionEvent event)
    {
        System.out.println("At the tone, the time is "
        + Instant.ofEpochMilli(event.getWhen()));
        Toolkit.getDefaultToolkit().beep();
    } 
}

Timer类可以实现经过一段时间就得到通知
定时器要求传递的对象所属的类实现了java.awt.event包中的ActionListener接口

当到达指定的时间间隔之后, 定时器就会调用actionPerformed方法

API

javax.swing.JOptionPane
static void showMessageDialog​(Component parentComponent, Object message)
Brings up an information-message dialog titled “Message”.
static void showMessageDialog​(Component parentComponent, Object message, String title, int messageType)
Brings up a dialog that displays a message using a default icon determined by the messageType parameter.
static void showMessageDialog​(Component parentComponent, Object message, String title, int messageType, Icon icon)
Brings up a dialog displaying a message, specifying all parameters.
javax.swing.Timer
Timer​(int delay, ActionListener listener)
Creates a Timer and initializes both the initial delay and between-event delay to delay milliseconds.
void start()
Starts the Timer, causing it to start sending action events to its listeners.
void stop()
Stops the Timer, causing it to stop sending action events to its listeners.
java.awt.Toolkit
static Toolkit getDefaultToolkit()
Gets the default toolkit.
abstract void beep()
Emits an audio beep depending on native system settings and hardware capabilities.

Comparator接口

String类实现了Comparable< String >, 使其可以按照字典顺序排序
若要实现按照长度递增顺序进行排序
若要处理这种情况: Arrays.sort方法可以使用一个数组和一个比较器作为参数, 比较器是实现了Comparator接口的类的实例

public interface Comparator<T>
{
	int compare(T first, T second);
}

若要实现按长度比较字符串, 如下实现Comparator< String >的类

class LengthComparator implements Comparator<String>
{
	public int compare(String first, String second)
	{
		return first.length() - second.length();
	}
}

单个的实例进行比较时:

var comp = new LengthComparator();
if (comp.compare(words[i], words[j]) > 0).....

若对一个数组排序:

String[] friends = {"Peter", "Paul", "Mary"};
Arrays.sort(friends, LengthComparator());

对象克隆

clone是一个protected方法, 代码无法直接调用
Object类的clone方法, 是逐字节进行复制, 拷贝对象会得到相同子对象的另一个引用
通常子对象都是可变的, 所以必须重新定义clone方法建立深拷贝(deep copy)

  1. 实现Cloneable接口
  2. 重新定义clone方法, 并指定public修饰符

Cloneaable接口是java少数的**标记接口(tagging interface)**之一, 标记接口不包含任何方法, 只是起到一个允许在类型查询中使用instanceof的作用
clone/CloneTest.java

package clone;
/**
 * This program demonstrates cloning.
 * @author Cay Horstmann
 */

public class CloneTest 
{
    public static void main(String[] args)
    throws CloneNotSupportedException
    {
        var original = new Employee("John Q. Public", 50000);
        original.setHireDay(2000, 1, 1);
        Employee copy = original.clone();
        copy.raiseSalary(10);
        copy.setHireDay(2002, 12, 31);
        System.out.println("original=" + original);
        System.out.println("Copy=" + copy);
    }
}

clone/Employee.java

package clone;

import java.util.Date;
import java.util.GregorianCalendar;

public class Employee implements Cloneable 
{
    private String name;
    private double salary;
    private Date hireDay;

    public Employee(String name, double salary)
    {
        this.name = name;
        this.salary = salary;
        hireDay = new Date();
    }

    public Employee clone() throws CloneNotSupportedException
    {
        // call Object.clone()
        Employee cloned = (Employee) super.clone();
        //clone mutable fields
        cloned.hireDay = (Date) hireDay.clone();

        return cloned;
    }

    /**
     * Set the hire day to a given date.
     * @param year the year of the hire day
     * @param month the month of the hire day
     * @param day the day of the hire day
     */
    public void setHireDay(int year, int month, int day)
    {
        Date newHireDay = new GregorianCalendar(year, month - 1, day).getTime();
        // example of instance field mutation
        hireDay.setTime(newHireDay.getTime());
    }
    public void raiseSalary(double byPercent)
    {
        double raise = salary * byPercent / 100;
        salary += raise;
    }
   

    public String toString()
    {
        return "Employee[name=" + name + ", salary=" + salary + ", hireDay=" + hireDay + "]";
    }
}

lambda表达式

lambda表达式是一个可传递的代码块, 可以在以后执行一次或多次

lambda表达式的语法

lambda表达式形式: 参数 ->箭头, 以及表达式, 如果代码无法放在一个表达式里, 那就放在一个代码块里, 包含显式return 语句

(String first, String second) ->
{
	if(first.length() < second.length()) return -1;
	else if(first.length() > second.length()) return 1;
	else return 0;
}

如果lambda表达式无参数, 仍然需要小括号
如果可以推导出lambda表达式参数类型, 则可以省略参数类型


(first, second) ->
{
	if(first.length() < second.length()) return -1;
	else if(first.length() > second.length()) return 1;
	else return 0;
}

如果只有一个参数且参数类型可推导, 则可省略小括号
无需指定lambda表达式返回类型

package lambda;

import java.util.*;
import javax.swing.*;
import javax.swing.Timer;

/**
 * This program demonstrates the use of lambda expressions.
 * @author Car Horstmann
 */

public class LambdaTest 
{
    public static void main(String[] args)
    {
        var planets = new String[] {"Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"};
        System.out.println(Arrays.toString(planets));
        System.out.println("Sorted in dictionary order:");
        Arrays.sort(planets);
        System.out.println(Arrays.toString(planets));
        System.out.println("Sorted by length:");
        Arrays.sort(planets, (first, second) -> first.length() - second.length());
        System.out.println(Arrays.toString(planets));


        var timer = new Timer(1000, event->
            System.out.println("The time is" + new Date()));

        timer.start();

        //keep program running until user selects "OK"
        JOptionPane.showMessageDialog(null, "Quit program?");
        System.exit(0);
    }
}

函数式接口

对于只有一个抽象方法的接口, 可以提供一个lambda表达式, 这种接口称之为函数式接口(functional interface)
与使用传统内联类相比, 效率更高
java中对lambda表达式所能做的只有转换为函数式接口
java中有很多定义了的通用的函数式接口, 其中BiFunction< T, U, R > 就可以保存字符串比较的lambda表达式

BiFunction<String, String, Integer> comp
	= (first, second) -> first.length() - second.length();

然而这样并不能直接用在Arrays.sort中
java.util.function包中的Predicate接口专门用来传递lambda表达式

public interface predicate<T>
{
	boolean test(T t);
	//additional default and static methods
}

ArrayList类的removeif方法参数就是Predicate。

list.removeif(e -> e == null);

供应者(Supplier) 无参数, 调用时生成一个< T >类型的值, 用于实现懒计算

LocalDate hireDay = Objects.requireNonNullOrElse(day, new LocalDate(1970, 1, 1));

然而day通常很少为空, 通过使用供应者, 即可延迟构造默认的LocalDate的计算

LocalDate hireday = Objects.requireNonNullOrElseGet(day, 
	() -> new LocalDate(1970, 1, 1))

方法引用

var timer = new Timer(1000, System.out::println);

表达式System.out::prntln是一个方法引用(method reference), 指示编译器生成一个函数式接口实例
方法引用也不是对象

Arrays.sort(string, String::compareToIgnoreCase)

只有把lambda表达式的体只调用一个方法而不做其他操作时才能把lambda表达式重写为方法引用

list.removeIf(Object::isNull);

可以在方法中引用this参数

class Greeter
{
	public void greet(ActionEvent event)
	{
		System.out.println("Hello, the time is" + Istant.ofEpochMIlli(event,getWhen()));
	}
}
class RepeatedGreeter extends Greeter
{
	public void greet(ActionEvent event)
	{
		var timer = new Timer(1000, super::greet);
		timer.start();
	}
}

构造器引用

ArrayList <String> names = ....;
Stream<Person> stream = names.stream().map(Person::new);
List <Person> people = stream.collect(Collectors.toList());

map方法为各个列表元素调用Person(String)构造器
可以用数组类型建立构造器引用, 例如:

int[]::new

java无法构造泛型类型T的数组, 因为会转化为new Object[n]
若希望得到一个Person引用数组, 而不是Object引用数组, 可以把Person[]::new传入toArray方法

Person[] people = stream.toArray(Person[]::new);

变量作用域

lambda表达式可以访问外围方法和类中的变量
访问的变量被lambda表达式捕获(captured), 在java中lambda表达式就是闭包(closure)
但是访问的变量不可在表达式中更改, 所在的类也不能更改,即必须是事实最终变量(efectively final)
同样, lambda表达式中声明的变量具有与嵌套块相同的作用域
在lambda表达式中的this参数指的是创建该表达式的方法的this参数。

处理lambda表达式

使用lambda表达式的重点是延迟执行(deferred execution)
如下情况可考虑使用:

  1. 在一个单独的线程运行代码
  2. 多次运行代码
  3. 在算法适当位置运行代码
  4. 发生某种情况时执行代码
  5. 只在必要时执行代码
repeat(10, () -> System.out.println("Hello, World!"));

public static void repeat(int n, Runnalble action)
{
	for (int i = 0; i < n; i++)	action.run();
}

在这里插入图片描述
倘若希望知道这个动作出现再哪一次迭代中, 需要一个接口, 其中的方法接受int参数, 返回void

public interface IntConsumer
{
	void appcpt(int value);
}

改进版本如下:

public static void repeat(int n, IntCunsumer action)
{
	for (int i = 0; i < n; i++) action.accept(i);
}

repeat(10, i -> System.out.println("Countdown: " + (9 - i)));
函数式接口参数类型返回类型抽象方法名
BooleanSupplierbooleangetAsBoolean
PSupplierpgetAsP
PConsumerpvoidaccepet
ObjPConsumer< T >T, pvoidaccept
PFunction< T >pTapply
PToQFunctionpqapplyAsQ
ToPFunction< T >TpapplyAsP
ToPBiFunction<T, U>T, UpapplyAsP
PUnaryOperatorppapplyAsP
PBinaryOperatorp, ppapplyAsP
PPredicatepbooleantest

p, q是int, long, double; P, Q是Int, Long, Double

在自己定义的函数式接口可以用@FunctionalInterface来注解

再谈Comparator

Comparator接口包含很多静态方法来创建比较器
comparing方法将类型T
假设有一个Person数组, 可按如下对名称排序

Arrays.sort(people, Comparator.comparing(Person::getName));

可以将比较器和thenComparing方法连接, 处理比较结果相同的局面

Arrays.sort(people,
	Comparator.comparing(Person::getLastName)
	.thenComparing(Person::getFirstName);
)

还可以给comparing和thenComparing方法提取的键指定一个比较器
如下根据人名长度进行排序

Arrays.sort(people, Comparator.comparing(Person::getName, 
(s, t) -> Integer.compare(s.length(), t.length())));

还有一种更简单的方法

Arrays.sort(people, Comparator.comparingInt(p -> p.getName().length()));

如果键函数可以返回null, 可能会用到nullsFirst和nullsLast适配器, 这些静态方法会修改现有比较器, 使其在遇到null值不抛出异常。
nullsFirst方法需要一个·比较器, 而naturalOrder方法可以为实现了Comparable的类建立一个比较器

Arrays.sort(people, comparing(Person::getMiddleName, nullsFirst(naturalOrder())));

reverseOrder方法提供自然顺序的逆序, 亦可以使用reversed实例方法

内部类

内部类(innner class)是定义在另一个类中的类.

  1. 内部类可以对同一个包中的其它类隐藏
  2. 内部类方法可以访问定义这个类的作用域中的数据, 包括原本私有的数据

使用内部类访问对象状态

public class TalkingClock
{
	private int interval;
	private boolean beep;
	public TalkingClock(int interval, boolean beep){....}
	public void start() {...}
	
	public class TimePrinter implements ActionListener
	//an inner class
	{
		...
	}
}
public class TimePrinter implements ActionListener
{
	public void actionPerformed(ActionEvent event)
	{
		System.out.println("At the tone, time is"
		+ Instant.ofEpochMilli(event, getWhen()));
		+ if (beep) Toolkit.getDefaultToolkit().beep();
	}
}

也可以把TimePrinter类声明为private

innerClass/InnerClassTest.java

package innerClass;
 
import java.awt.*;
import java.awt.event.*;
import java.time.*;

import javax.swing.*;

/**
 * This program demonstrates the use of inner classes.
 * @author Cay Horstmann
 */
public class InnerClassTest 
{
    public static void main(String[] args)
    {
        var clock = new TalkingClock(1000, true);
        clock.start();

//keep program running until the user selects "OK"
        JOptionPane.showMessageDialog(null, "Quit program?");
        System.exit(0);
    }
    
    
}

class TalkingClock
{
    private int interval;
    private boolean beep;
    /**
     * Constructs a talking clock
     * @param interval the interval between messages (in milliseconds)
     * @param beep true if the clock should beep
     */
    public TalkingClock(int interval, boolean beep)
    {
        this.interval = interval;
        this.beep = beep;
    }
    /**
     * Starts the clock.
     */
    public void start()
    {
        var listener = new TimePrinter();
        var timer = new Timer(interval, listener);
        timer.start();
    }
    public class TimePrinter implements ActionListener
    {
        public void actionPerformed(ActionEvent event)
        {
            System.out.println("At the tone. the time is"
                + Instant.ofEpochMilli(event.getWhen()));
            if(beep) Toolkit.getDefaultToolkit().beep();
        }
    }
}

lambda表达式版本:

package innerClass;
 
import java.awt.*;
import java.time.*;

import javax.swing.*;
import javax.swing.Timer;

/**
 * This program demonstrates the use of inner classes.
 * @author Cay Horstmann
 */
public class InnerClassTest 
{
    public static void main(String[] args)
    {
        var clock = new TalkingClock(1000, true);
        clock.start();
        JOptionPane.showMessageDialog(null, "Quit program?");
        System.exit(0);
    } 
}

 class TalkingClock
{
    private int interval;
    private boolean beep;
    /**
     * Constructs a talking clock
     * @param interval the interval between messages (in milliseconds)
     * @param beep true if the clock should beep
     */
    public TalkingClock(int interval, boolean beep)
    {
        this.interval = interval;
        this.beep = beep;
    }
    /**
     * Starts the clock.
     */
    public void start()
    {
        var timer = new Timer(interval, event -> 
        {
            System.out.println("At the tone. the time is"
            + Instant.ofEpochMilli(event.getWhen()));
      if(beep) Toolkit.getDefaultToolkit().beep();
        });
       timer.start();
    }
}

内部类的特殊语法规则

使用外围类引用的正规语法如下:

OuterClass.this

表示外围类引用

public void actionPerformed(ActionEvent event)
{
	...
	if(TalkingClock.this.beep) Toolkit.getDefaultToolkit().beep();
}

可以采用如下语法编写内部类对象的构造器

outerObject.new InnerClass(constructor parameters)
ActionListener listener = this.new TimePrinter();

此处创建的TimePrinter对象的外围类引用被设置为创建内部类对象的方法的this引用。
也可以显式命名将外围类引用设置为其他的对象

var jabberer = new TalkingClock(1000, true);
TalkingClock.TimerPrinter listener = jabberer.new TimerPrinter();

在外围类之外, 可以如此引用内部类

OuterClass.InnerClass

内部类声明的所有静态字段都必须是final。 并初始化为一个编译时常量。
内部类最好不能有static方法。

内部类是否有用, 必要和安全

内部类只是个编译器现象, 与虚拟机无关, 编译器将内部类转换为常规的类文件, 用$分隔外部类名和内部类名。
编译器可以自动实现对外围变量的引用,访问外围类的私有数据, 但我们无法自己编写程序实现这种机制
实际上在TimePrinter类的actionPerformed方法中

if (beep)

实际上产生以上调用

if (TalkingClock.access&0(outer));

这种做法的确会带来安全风险, 任何人都可以调用access$0轻易读取私有字段beep。
若内部类访问了私有数据字段, 就i有可能通过外围类所在包增加的其他类访问那些私有字段

局部内部类

当只需要调用某个内部类仅一次时, 可以在一个方法中局部定义该类

public void start()
{
	class TimePrinter implements ActionListener
	{
		public void actionPerformed(ActionEvent event)
		{
			System.out.println("At the tone, the time is "
			 	+ Instant.ofEpochMilli(event.getWhen()));
			 	if(beep) Tookit.getdefaultToolkit().beep();
		}
	}
	var listener = new TimePrinter();
	var timer = new Timer(interval, listener);
	timer.start();
}

声明局部类时不能有访问说明符(public 和 private), 局部类的作用域被限定在声明这个局部类的块中。
该局部类除了start方法, 没有任何方法知道TimePrinter类的存在。

由外部方法访问变量

局部类不仅可以访问外部类字段, 还可以访问局部变量, 前提是必须是**事实最终变量(effectively final)。

public void start(int interval, boolean beep)
{
	class TimePrinter implements ActionListener
	{
		public void actionPerformed(ActionEvent event)
		{
			System.out.println("At the tone, the time is "
				+ Instant.ofEpochMilli(event.getWhen()));
				if (beep) Toolkit.getDefaultToolkit().beep();
		}
	}
	var listener = new TimePrinter();
	var timer = new Timer(interval, listener);
	timer.start();
}

TimePrinter类在beep参数值消失前将beep字段复制为start方法的局部变量。
TimePrinter类会自动生成一个构造器, 将beep值传入, 将外部的实例传入, 构造
编译器检测对局部变量的访问, 为每一个变量建立相应的实例字段, 并将局部变量复制到构造器, 从而初始化这些实例字段。

匿名内部类

若只想创建这个类的一个对象, 可以不为该内部类指定名字, 这样的类称为匿名内部类(anonymous inner class)
匿名内部类不能有构造器, 构造参数会传给**超类(super class)**构造器

var count = new Person("Dracula") {...};
	//an object of an inner class extending Person

通常使用匿名内部类实现事件监听器和其他回调。
不过最好还是使用lambda表达式。
annonymousInnerClass / AnonymousInnerClassTest.java


#include <stdio.h>

#define N 1000000

long long stack[N][3] = { 0 };


long long Marge(long long* restrict num, const long long  n);

int main(void)
{
   long long int retnum;
   long long  n;
   long long num[N];
   scanf("%lld", &n);
   for (long long i = 0; i < n; i++)
   	scanf("%lld", &num[i]);
   retnum = Marge(num, n);

   printf("%lld", retnum);
   return 0;
}

long long Marge(long long* restrict num, const long  long n)
{
   long long val;
   long long stackPoint = 0;
   long long i;
   for (i = 0; i < n; i++)
   {
   	if (num[i] == 0)
   		continue;
   	if (stackPoint > 0 && stack[stackPoint - 1][1] >= i + num[i])
   		continue;
   	stack[stackPoint][0] = i - num[i];
   	stack[stackPoint][1] = i + num[i];
   	stack[stackPoint][2] = num[i];
   	while (stackPoint > 0 && stack[stackPoint][0] <= stack[stackPoint - 1][0])
   	{
   		stack[stackPoint - 1][0] = stack[stackPoint][0];
   		stack[stackPoint - 1][1] = stack[stackPoint][1];
   		stack[stackPoint - 1][2] = stack[stackPoint][2];
   		stackPoint--;
   	}
   	while (stackPoint > 0 && stack[stackPoint - 1][1] >= stack[stackPoint][0])
   	{
   		long long temp = stack[stackPoint][1] - stack[stackPoint - 1][0];
   		temp += temp & 1 ? 1 : 0;
   	//	printf("%lld\n", temp);
   		stack[stackPoint - 1][2] = temp  / 2;
   	//	stack[stackPoint - 1][0] = stack[stackPoint][0] < stack[stackPoint - 1][0] ? stack[stackPoint][0] : stack[stackPoint - 1][0];
   	//	stack[stackPoint - 1][1] = stack[stackPoint][1] > stack[stackPoint - 1][1]? stack[stackPoint][1]: stack[stackPoint - 1][1];
   	    stack[stackPoint - 1][1] = stack[stackPoint][1];
   		stackPoint--;
   	}
   	stackPoint++;
   }
   for (i = 0, val = 0; i < stackPoint; i++)
   {
//		printf("%lld %lld %lld\n", stack[i][0], stack[i][1], stack[i][2]);
   	val += stack[i][2];
   }
   return val;
}

假设有如下构造

var friends = new  ArrayList<String> ();
friends.add("Harry");
friends.add("Tony");
invite(friends);

然而在此之后不再需要该数组列表, 最好将其作为一个匿名列表

invite(new ArrayList<String>() {{ add("Harry");  add("Tony"); }});

以上技巧称之为“双括号初始化(double brace initialization)
System.err.prinln()对静态方法反馈不奏效, 因为静态方法没有this

new Object(){}.getClass().getEnclosingClass()	//gets class of static method

静态内部类

静态内部类可以把一个类隐藏在另一个类内部, 并不需要去引用外围类的东西。
非静态内部类不可生成内部类对象。

package staticInnerClass;

/**
 * This program demonstrates the use of static inner classes.
 * @author Cay Horstmann
 */
public class StaticInnerClassTest 
{
    public static void main(String[] args)
    {
        var values = new double[20];
        for (int i = 0; i < values.length; i++)
            values[i] = 100 * Math.random();
        ArrayAlg.Pair p = ArrayAlg.minmax(values);
        System.out.println("min = " + p.getFirst());
        System.out.println("max = " + p.getSecond());
    }
}

class ArrayAlg
{
    /**
     * A pair of floating-point numbers
     */
    public static class Pair
    {
        private double first;
        private double second;

        /**
         * Constructs a pair from two floating-point numbers
         * @param f the first number
         * @param s the second number
         */

        public Pair(double f, double s)
        {
            first = f;
            second = s;
        }

        /**
         * Returns the first number of the pair
         * @return the first number
         */
        public double getFirst()
        {
            return first;
        }
        /**
         * Returns the second number of the pair
         * @return the second number
         */
        public double getSecond()
        {
            return second;
        }
    }

    /**
     * Computes both the mininum and the maximum of an array
     * @param values an array of floating-poing numbers
     * @return a pair whose first element is the minimum and whose second element
     * is the maximum
     */

     public static Pair minmax(double[] values)
     {
         double min = Double.POSITIVE_INFINITY;
         double max = Double.NEGATIVE_INFINITY;
         for(double v: values)
         {
             if(min > v) min = v;
             if(max < v) max = v;
         }
         return new Pair(min, max);
     }
}

服务加载器

有时会开发采用服务架构的应用, jdk提供了一个加载服务的简单机制。
利用ServiceLoader类可以很简单的加载一个符合公众接口的服务
定义一个接口(愿意也可以定义一个超类), 其中包含服务的各个实例应当提供的方法

package serviceLoader;

public interface Cipher
{

    byte[] encrypt(byte[] source, byte[] key);
    byte[] decrypt(byte[] source, byte[] key);
    int strength();
}

服务提供者可以提供一个或多个实现这个服务的类


package serviceLoader.impl;

public class CaesarCipher implements Cipher
{
    public byte[] encrypt(byte[] source, byte[] key)
    {
        var result = new byte[source.length];
        for (int i = 0; i < source.length; i++)
            result[i] = (byte) (source[i] + key[0]);
        return result;
    }

    public byte[] decrypt(byte[] source, byte[] key)
    {
        return encrypt(source, new byte[] {(byte) - key[0]});
    }
    public int strength() {return 1 ; }
}

每个实现类至少有一个无参数构造器,
现在把这些类的类名增加到一个utf-8编码文本文件中, 文件名必须跟完全限定类名一致
如上, 其中必需加入

serviceLoader.impl.CaesarCipher

完成这些后, 程序可以如下初始化一个服务加载器

public static ServiceLoader<Cipher> cipherLoader= ServiceLoader.load(Cipher.class);

服务加载器的iterator方法返回一个迭代器来迭代处理所提供的所有服务实现。最容易的做法是使用一个增强的for循环进行遍历

public static getCipher(int minStrength)
{
	for(Cipher cipher : cipherLoader)		//implicitly calls cipherLoader.iterator()
	{
		if(cipher.strength() >= minStrength) return cipher;
	}
}

代理(proxy)

代理在编译时期无法确定到底实现哪个接口时才有必要使用

何时使用代理

若想构造一个具体的类, 使用newInstance方法或使用反射找出构造器即可, 但是不能实例化接口, 代理类可以在运行时创建全新的类, 代理类包含以下方法

  1. 指定接口所需的全部方法
  2. Object类中的所有方法
    必须为代理提供一个**调用处理器(invocation handler), 调用处理器是实现了InvocationHandler的接口的类的对象
Object invoke(Object proxy, Method method, Object[] args)

当调用代理对象的方法时, invoke方法必会被调用。

创建代理对象

创建一个对象需要使用Proxy类的newProxyInstance方法, 其需要三个参数

  1. 一个类加载器
  2. 一个Class对象数组, 每个元素对应实现的各个接口
  3. 一个调用处理器
package proxy;

import java.lang.reflect.*;
import java.util.*;

/**
 * This program demonstrates the use of proxies.
 * @author Cay Horstmann
 */

public class ProxyTest 
{
    public static void main(String[] args)
    {
        var elements = new Object[1000];

        //fill elements with the proxies for the integer 1...1000
        for (int i = 0; i < elements.length; i++)
        {
            Integer value = i + 1;
            var handler = new TraceHandler(value);
            Object proxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), 
            new Class[] {Comparable.class}, handler);
            elements[i] = proxy;
        }
        //construct a random integer
        Integer key = new Random().nextInt(elements.length) - 1;

        //search for the key
        int result = Arrays.binarySearch(elements, key);

        //print match if found
        if(result >= 0)
        System.out.println(elements[result]);
    }
    
}


/**
 * An invocation handler that prints out the method name and parameters, then
 * invokes the original method
 */

 class TraceHandler implements InvocationHandler
 {
     private Object target;

     /**
      * Constructs a TraceHandler
      * @param t the implicit parameter of the method call
      */
      public TraceHandler(Object t)
      {
          target = t;
      }
      
      public Object invoke(Object proxy, Method m, Object[] args) 
      throws Throwable
      {
          //print implicit arguments
          System.out.print(target);
          //print method name
          System.out.print("." + m.getName() + "(");
          //print explicit arguments
          if(args != null)
          {
              for (int i = 0; i < args.length; i++)
              {
                  System.out.print(args[i]);
                  if(i < args.length - 1) System.out.print(", ");
              }
          }
          System.out.println(")");

          //invoke actual method
          return m.invoke(target, args);
      }
 }

然而toString方法也同样被代理, 某些Object方法总是会被代理

代理类的性质

所有的代理类都会扩展Proxy类, 一个代理类只有一个字段, 那就是调用处理器
所有的代理方法都会覆盖Object类的toString, equals和hashCode方法, 其他方法不覆盖。

对一个特定的类加载器和预设的一组接口, 有且只有一个代理类
代理类总是final 和 public。

API

java.lang.reflect.InvocationHandler
Object invoke​(Object proxy, Method method, Object[] args)
Processes a method invocation on a proxy instance and returns the result.
java.lang.reflect.Proxy
static Class<?> getProxyClass​(ClassLoader loader, Class<?>… interfaces)
Deprecated! Proxy classes generated in a named module are encapsulated and not accessible to code outside its module.
static Object newProxyInstance​(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
Returns a proxy instance for the specified interfaces that dispatches method invocations to the specified invocation handler.
static InvocationHandler getInvocationHandler​(Object proxy)
Returns the invocation handler for the specified proxy instance.
static boolean isProxyClass​(Class<?> cl)
Returns true if the given class is a proxy class.
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值