【Java8】 方法引用 讲解_java8 方法引用详解_SoWhat1412的博客-程序员秘密

技术标签: java  # Java8特性  

一、概述

在学习 lambda 表达式之后,我们通常使用 lambda 表达式来创建匿名方法。然而,有时候我们仅仅是调用了一个已存在的方法。如下:

Arrays.sort(stringsArray,(s1,s2)->s1.compareToIgnoreCase(s2));

 在Java8中,我们可以直接通过方法引用简写 lambda 表达式中已经存在的方法。

Arrays.sort(stringsArray, String::compareToIgnoreCase);

这种特性就叫做方法引用(Method Reference)。

二、什么是方法引用

     方法引用是用来直接访问类或者实例的已经存在的方法或者构造方法。方法引用提供了一种引用而不执行方法的方式,它需要由兼容的函数式接口构成的目标类型上下文。计算时,方法引用会创建函数式接口的一个实例

当Lambda表达式中只是执行一个方法调用时,不用Lambda表达式,直接通过方法引用的形式可读性更高一些。方法引用是一种更简洁易懂的Lambda表达式。

注意方法引用是一个Lambda表达式,其中方法引用的操作符是双冒号"::"。

简单地说,就是一个Lambda表达式。在Java 8中,我们会使用Lambda表达式创建匿名方法,但是有时候,我们的Lambda表达式可能仅仅调用一个已存在的方法,而不做任何其它事,对于这种情况,通过一个方法名字来引用这个已存在的方法会更加清晰,Java 8的方法引用允许我们这样做。方法引用是一个更加紧凑,易读的Lambda表达式,注意方法引用是一个Lambda表达式,其中方法引用的操作符是双冒号"::"

三、方法引用例子

先看一个例子,首先定义一个Person类,如下:

package mytest;

import java.time.LocalDate;
import java.util.Arrays;
import java.util.Comparator;



class Person
{

	public Person(String name, LocalDate birthday)
	{
		this.name = name;
		this.birthday = birthday;
	}

	String name;
	LocalDate birthday;

	public LocalDate getBirthday()
	{
		return birthday;
	}

	//  字符串的对比用如下方法
	public static int compareByAge(Person a, Person b)
	{
		return a.birthday.compareTo(b.birthday);
	}

	@Override
	public String toString()
	{
		return this.name;
	}
}

/**
 * 假设我们有一个Person数组,并且想对它进行排序,这时候,我们可能会这样写:原始写法,使用匿名类:
 */

public class CRacer
{
	public static void main(String[] args)
	{
		Person[] pArr = new Person[]{
				new Person("003", LocalDate.of(2016, 9, 1)),
				new Person("001", LocalDate.of(2016, 2, 1)),
				new Person("002", LocalDate.of(2016, 3, 1)),
				new Person("004", LocalDate.of(2016, 12, 1))};

		// 使用匿名类 实现 对比函数
		Arrays.sort(pArr, new Comparator<Person>()
		{
			@Override
			public int compare(Person a, Person b)
			{
				return a.getBirthday().compareTo(b.getBirthday());
			}
		});

		System.out.println(Arrays.asList(pArr));

	}


}

其中,Arrays类的sort方法定义如下:

public static <T> void sort(T[] a, Comparator<? super T> c)

这里,我们首先要注意Comparator接口是一个函数式接口,因此我们可以使用Lambda表达式,而不需要定义一个实现Comparator接口的类,并创建它的实例对象,传给sort方法。

package mytest;

import java.time.LocalDate;
import java.util.Arrays;
import java.util.Comparator;


class Person
{

	public Person(String name, LocalDate birthday)
	{
		this.name = name;
		this.birthday = birthday;
	}

	String name;
	LocalDate birthday;

	public LocalDate getBirthday()
	{
		return birthday;
	}

	//  字符串的对比用如下方法
	public static int compareByAge(Person a, Person b)
	{
		return a.birthday.compareTo(b.birthday);
	}

	@Override
	public String toString()
	{
		return this.name;
	}
}

/**
 * 假设我们有一个Person数组,并且想对它进行排序,这时候,我们可能会这样写:原始写法,使用匿名类:
 */

public class CRacer
{
	public static void main(String[] args)
	{
		Person[] pArr = new Person[]{
				new Person("003", LocalDate.of(2016, 9, 1)),
				new Person("001", LocalDate.of(2016, 2, 1)),
				new Person("002", LocalDate.of(2016, 3, 1)),
				new Person("004", LocalDate.of(2016, 12, 1))};

		// 使用匿名类 实现 对比函数
		Arrays.sort(pArr, new Comparator<Person>()
		{
			@Override
			public int compare(Person a, Person b)
			{
				return a.getBirthday().compareTo(b.getBirthday());
			}
		});

		System.out.println(Arrays.asList(pArr));


		//使用lambda表达式
		Arrays.sort(pArr, (Person a, Person b) -> a.getBirthday().compareTo(b.getBirthday()) );

		System.out.println(Arrays.asList(pArr));

		// 使用已经有的对比条件
		//使用lambda表达式和类的静态方法
		Arrays.sort(pArr, (a ,b) -> Person.compareByAge(a, b));

		System.out.println(Arrays.asList(pArr));

		// 使用方法引用 实现
		//使用方法引用,引用的是类的静态方法  方法不需要加括号
		Arrays.sort(pArr, Person::compareByAge);

		System.out.println(Arrays.asList(pArr));

	}
}

在以上代码中,方法引用 Person::compareByAge 在语义上与Lambda表达式 (a, b) -> Person.compareByAge(a, b) 是等同的,都有如下特性:

  • 真实的参数是拷贝自Comparator<Person>.compare方法,即(Person, Person);
  • 表达式体调用Person.compareByAge方法。

四、四种方法引用类型

方法引用的标准形式是:类名::方法名。(注意:只需要写方法名,不需要写括号

有以下四种形式的方法引用:

类型 示例
引用静态方法 ContainingClass::staticMethodName
引用某个对象的实例方法 containingObject::instanceMethodName
引用某个类型的任意对象的实例方法 ContainingType::methodName
引用构造方法 ClassName::new

 

 

 

 

 

使用方法引用的主要步骤

  1. 定义一个函数式接口
  2. 定义一个满足函数式接口抽象方法要求的方法
  3. 使用对步骤2中定义的(x::y)方法引用实例化函数式接口的实例(注:静态方法的方法引用格式为 类名::方法名;实例方法的方法引用格式为 对象实例名::方法名)
  4. 使用函数式接口实例调用方法:Instance.AbstractMethod();

1、静态方法引用

组成语法格式:ClassName::staticMethodName

我们前面举的例子Person::compareByAge就是一个静态方法引用

注意:

  • 静态方法引用比较容易理解,和静态方法调用相比,只是把 换为 ::
  • 在目标类型兼容的任何地方,都可以使用静态方法引用。

例子:

  String::valueOf   等价于lambda表达式 (s) -> String.valueOf(s)

  Math::pow          等价于lambda表达式  (x, y) -> Math.pow(x, y);

字符串翻转例子:

package mytest;

interface StringFunc
{
	String func(String n);
}

class MyStringOps
{
	public static String strReverse(String str)
	{
		StringBuilder result = new StringBuilder();
		for (int i = str.length() - 1; i >= 0; i--)
		{
			result.append(str.charAt(i));
		}
		return result + "";
	}
}

public class CRacer
{
	public static String stringOp(StringFunc sf, String s)
	{
		return sf.func(s);
	}

	public static void main(String[] args)
	{
		String inStr = "labmda add power to Java";
		//MyStringOps::strReverse 相当于实现了接口方法func()
		// 并在接口方法func()中作了MyStringOps.strReverse()操作
		//表达式MyStringOps::strReverse的计算结果为对象引用,其中,strReverse提供了StringFunc的func()方法的实现。
		String outStr = stringOp(MyStringOps::strReverse, inStr);
		System.out.println("Original string: " + inStr);
		System.out.println("String reserved: " + outStr);

	}
}

查找 最大值 demo

package mytest;

import java.util.ArrayList;
import java.util.Collections;

class MyClass
{

	private int val;

	MyClass(int v)
	{
		this.val = v;
	}

	public int getValue()
	{
		return this.val;
	}

}

public class CRacer
{
	public static int compareMC(MyClass a, MyClass b)
	{
		return a.getValue() - b.getValue();
	}

	public static void main(String[] args)
	{
		ArrayList<MyClass> a1 = new ArrayList<MyClass>();
		a1.add(new MyClass(1));
		a1.add(new MyClass(4));
		a1.add(new MyClass(2));
		a1.add(new MyClass(9));
		a1.add(new MyClass(3));
		a1.add(new MyClass(7));
		//UseMethodRef::compareMC 生成了抽象接口Comparator定义的compare()方法的实例。
       // UseMethodRef定义了静态方法compareMC(),它与Comparator定义的compare()方法兼容。
       //因此,没有必要显式的实现Comparator接口并创建其实例。
		MyClass maxValObj = Collections.max(a1, CRacer::compareMC);
		System.out.println("Maximum value is: " + maxValObj.getValue());
	}

}

2、特定实例对象的方法引用

这种语法与用于静态方法的语法类似,只不过这里使用对象引用而不是类名。实例方法引用又分以下三种类型:

2.1.实例上的实例方法引用

组成语法格式:instanceReference::methodName

如下示例,引用的方法是myComparisonProvider 对象的compareByName方法。

class ComparisonProvider{
    
    public int compareByName(Person a, Person b){
        return a.getName().compareTo(b.getName());
    }

    public int compareByAge(Person a, Person b){
        return a.getBirthday().compareTo(b.getBirthday());
    }
}

ComparisonProvider myComparisonProvider = new ComparisonProvider();
Arrays.sort(rosterAsArray, myComparisonProvider::compareByName);
      public static void main(String[] args) {
        String inStr = "lambda add power to Java";
        MyStringOps strOps = new MyStringOps();//实例对象
        //strOps::strReverse1 相当于实现了接口方法func() 
        String outStr = stringOp(strOps::strReverse1, inStr);

        System.out.println("Original string: " + inStr);
        System.out.println("String reserved: " + outStr);
    }
唯一区别就是一个传入的是 class:StaticFun  object:PublicFun

2.2超类上的实例方法引用

组成语法格式:super::methodName

方法的名称由methodName指定,通过使用super,可以引用方法的超类版本。

例子:

还可以捕获this 指针,this :: equals  等价于lambda表达式  x -> this.equals(x);

2.3.类型上的实例方法引用

组成语法格式:ClassName::methodName

注意:

若类型的实例方法是泛型的,就需要在::分隔符前提供类型参数,或者(多数情况下)利用目标类型推导出其类型。

静态方法引用和类型上的实例方法引用拥有一样的语法。编译器会根据实际情况做出决定。一般我们不需要指定方法引用中的参数类型,因为编译器往往可以推导出结果,但如果需要我们也可以显式在::分隔符之前提供参数类型信息。

例子:

String::toString 等价于lambda表达式 (s) -> s.toString()

这里不太容易理解,实例方法要通过对象来调用,方法引用对应Lambda,Lambda的第一个参数会成为调用实例方法的对象。

在泛型类或泛型方法中,也可以使用方法引用。

package mytest;

interface MyFunc<T>
{

	int func(T[] als, T v);
}

class MyArrayOps
{

	public static <T> int countMatching(T[] vals, T v)
	{
		int count = 0;
		for (int i = 0; i < vals.length; i++)
		{
			if (vals[i] == v)
			{
				count++;
			}
		}
		return count;
	}

}

public class CRacer
{
	public static <T> int myOp(MyFunc<T> f, T[] vals, T v)
	{
		return f.func(vals, v);
	}

	public static void main(String[] args)
	{
		Integer[] vals = {1, 2, 3, 4, 2, 3, 4, 4, 5};
		String[] strs = {"One", "Two", "Three", "Two"};
		int count;
		//count = myOp(MyArrayOps::<Integer>countMatching, vals, 4); // 泛型 实现统计
		count = myOp(MyArrayOps::countMatching, vals, 4); // 泛型 实现统计
		System.out.println("vals contains " + count + " 4s");
		// count = myOp(MyArrayOps::<String>countMatching, strs, "Two");
		count = myOp(MyArrayOps::countMatching, strs, "Two");
		System.out.println("strs contains " + count + " Twos");
	}

}

分析:

      在程序中,MyArrayOps是非泛型类,包含泛型方法countMatching()。该方法返回数组中与指定值匹配的元素的个数。注意这里如何指定泛型类型参数。例如,在main()方法中,对countMatching()方法的第一次调用如下所示:count = myOp(MyArrayOps::<Integer>countMatching,vals,4); 这里传递了类型参数Integer。

      注意,参数传递发生在::的后面。这种语法可以推广。当把泛型方法指定为方法引用时,类型参数出现在::之后、方法名之前。但是,需要指出的是,在这种情况(和其它许多情况)下,并非必须显示指定类型参数,因为类型参数会被自动推断得出。对于指定泛型类的情况,类型参数位于类名的后面::的前面。

3、引用特定类型的任意对象的实例方法

在Java 8 In Action是这样介绍的,指向任意类型实例方法的方法引用(我觉得叫类的任意对象的实例方法引用更直观)。我开始一直想当然的就认为是类::实例方法这样就可以了,结果写了几个发现都用不了,看了官网给出的示例发现又可以,于是,作为当代优秀青年,怎么可能不解决这个问题呢。

官网给出的示例:

String[] stringArray = { "Barbara", "James", "Mary", "John", "Patricia", "Robert", "Michael", "Linda" };
Arrays.sort(stringArray, String::compareToIgnoreCase);

 方法引用的等效lambda表达式 String::compareToIgnoreCase 将具有形式参数列表(String a, String b),其中a和b是用于更好地描述此示例的任意名称。方法引用将调用该方法 a.compareToIgnoreCase(b)。之后再查了下,原文是这样的 “ reference to an instance method of an arbitrary object of a particular type ”  arbitrary 任意的, particular 特定的,翻译过来就是引用特定类型的任意对象的实例方法。于是乎,知道了类的实例方法调用是有讲究的。那么,有什么样的条件呢?

public class test1 {
    public static void main(String[] args) {
 
        List<Student> list = new ArrayList<>();
        list.add(new Student("Jack1", 88));
        list.add(new Student("Jack2", 81));
        list.add(new Student("Jack3", 82));
        list.add(new Student("Jack4", 83));
        list.add(new Student("Jack5", 84));
        list.add(new Student("Jack6", 85));
        list.sort(Student::compareByScore);
        System.out.println(list);
    }
}
 
class Student {
    private String name;
    private int score;
 
    public Student(){
 
    }
 
    public Student(String name,int score){
        this.name = name;
        this.score = score;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public int getScore() {
        return score;
    }
 
    public void setScore(int score) {
        this.score = score;
    }
 
    public int compareByScore(Student student){
        return this.getScore() - student.getScore();
    }
 
    @Override
    public String toString(){
        return this.name + "  " + this.score + "  ";
    }
}

list.sort接受一个Comparator,Comparator需要实现compare方法,lambda形式 (v1,v2)-> {dosth;return intval;}。这里的compareByScore就是这种lambda形式的方法引用。

如果将上面的compareByScore方法改成如下形式,为什么又要报错了,我不就多传了一个参数进去么,其他的啥也没做啊。
 

 public int compareByScore(Student student1, Student student2){
        return this.getScore() - student1.getScore();
    }

    不好意思,它要求接口方法的参数必须比引用方法的参数多一个。而且第一个参数要是该引用方法的所在类型的或其父类,除接口方法的第一个参数以外, 其余参数的类型要求一样。这一段话,有些难以理解,看下面的代码就会明白了(也就是Interface参数个数要比我们引用方法参数个数多一个)。

demo1

package mytest;

class Test1
{
	public void a()
	{
		System.out.println("just a test " + this.hashCode());
	}

	public static void main(String[] args)
	{
		MyInter m = Test1::a;
		System.out.println(m.hashCode());
		Test1  object = new Test1();
		System.out.println(object.hashCode());
		m.d(object);
	}
}

@FunctionalInterface
interface MyInter
{
	// 入参参数比Test1的a方法多一个,接口方法的第一个参数恰巧是调用引用方法的对象
	void d(Test1 b);
	//void d(int b); 这个会报错
}

demo2

class Test1
{
	public void a(int param1, int param2)
	{
		System.out.println("just a test");
	}

	public static void main(String[] args)
	{

		MyInter m = Test1::a;
	}
}

@FunctionalInterface
interface MyInter
{
	//该接口参数比上述的a方法参数数量多一个,除去第一个,其它类型一致(可兼容,如可以一个int,一个Integer)
	//且 interface 中 虚函数的一个参数一定是 引用方法所在类 类型
	void d(Test1 d, int param1, int param2);
}

demo3

class Father
{
	public void a(Integer param1, int param2)
	{
	}
}
==========================================
class Son extends Father
{

	public static void main(String[] args)
	{
		MyInter m = Son::a;  //  这个是不对的!
		m.fun(new Son(), 12, 13); // 实参  需要是 形参的同类或者子类
	}
}

@FunctionalInterface
interface MyInter
{
	//该接口参数比上述的a方法参数数量多一个,除去第一个,其它类型一致(可兼容,如可以一个int,一个Integer)
	// interface 虚函数第一个参数 类型
	void fun(Father d, int param1, int param2);
}

==========================================
class Son extends Father
{
	public static void main(String[] args)
	{
		MyInter m = Father::a; // 该类要是 interface 中的同类或父类
		m.fun(new Son(), 12, 13); // 实参  需要是 形参的同类或者子类
	}
}

@FunctionalInterface
interface MyInter
{
	//该接口参数比上述的a方法参数数量多一个,除去第一个,其它类型一致(可兼容,如可以一个int,一个Integer)
	/*
		interface第一个参数	引用函数类型	状态
		Father			Father		OK
		Son			Son		OK
		Son 			Father 		OK
		Father			Son		NO
	* */
	void fun(Son d, int param1, int param2);
}

demo4

class Test1
{
	public void a(Integer param1, int param2)
	{
	}

	public static void main(String[] args)
	{
		MyInter m = (j, k, l) -> j.a(k, l);
       //第一个参数为方法目标,其余参数为参数
	}
}

@FunctionalInterface
interface MyInter
{
	void d(Test1 d, int param1, int param2);
}

 这个指向任意类型实例方法的方法引用有两个要求
       第一点:接口方法(interface)的参数比引用方法的参数多一个
       第二点:接口方法的第一个参数恰巧是调用引用方法的对象,或者是引用方法类型的子类。

interface第一个参数 引用函数类型 状态是否正确
Father Father OK
Son Son OK
Son Father OK
Father Son NO

4、构造方法引用

构造方法引用又分构造方法引用和数组构造方法引用。

构造函数的方法引用,它们是对象初始化方法。
使用构造函数的方法引用的主要步骤有:

  1. 定义一个只有抽象方法的函数式接口,该interface 中抽象方法的返回类型与使用该对象进行构造函数引用的对象相同。
  2. 创建一个类,该类的构造函数与函数式接口的抽象方法匹配。
  3. 使用对步骤#2中定义的构造函数的方法引用,实例化函数式接口的实例。(构造函数的方法引用格式为 类名::new)
  4. 在步骤#2中使用构造函数引用实例化类的实例。

构造函数引用与泛型一起使用时会变得更有用。通过使用泛型工厂方法,可以创建各种类型的对象。可以结合下面例子重点理解下思想。

4.1.构造方法引用(也可以称作构造器引用)

组成语法格式:Class::new

构造函数本质上是静态方法,只是方法名字比较特殊,使用的是new 关键字

例子:

String::new, 等价于lambda表达式 () -> new String() 

package mytest;

interface MyFunc1
{

	MyClass func(int n);
	// 会有个类的构造方法来实现该函数

}

class MyClass
{

	private int val;

	MyClass(int v)
	{
		val = v;
	}

	MyClass()
	{
		val = 0;
	}

	public int getValue()
	{
		return val;
	}

}

public class CRacer
{
	public static void main(String[] args)
	{

		MyFunc1 myClassCons1 = MyClass -> new MyClass();
		// 跟下面同样效果  相当于用构造函数 实现了interface 的 虚函数 切记
		MyFunc1 myClassCons = MyClass::new;
		// 等价于 MyFunc1 myClassCons = new MyClass();
		MyClass mc = myClassCons.func(100);
		System.out.println("val in mc is: " + mc.getValue());
	}

}

4.2.数组构造方法引用

组成语法格式:TypeName[]::new

例子:

int[]::new 是一个含有一个参数的构造器引用,这个参数就是数组的长度。等价于lambda表达式  x -> new int[x]。

假想存在一个接收int参数的数组构造方法

IntFunction<int[]> arrayMaker = int[]::new;
int[] array = arrayMaker.apply(10) // 创建数组 int[10]
package mytest;

import java.util.function.Function;

interface MyFunc1
{
	MyClass func(int n);
	// 会有个类的构造方法来实现该函数
}

class MyClass
{

	private int val;

	MyClass(int v)
	{
		val = v;
	}

	MyClass()
	{
		val = 0;
	}

	public int getValue()
	{
		return val;
	}

}

public class CRacer
{
	public static void main(String[] args)
	{

		MyFunc1 myClassCons1 = MyClass -> new MyClass();
		// 跟下面同样效果  相当于用构造函数 实现了interface 的 虚函数 切记
		MyFunc1 myClassCons = MyClass::new;
		// 等价于 MyFunc1 myClassCons = new MyClass();
		MyClass mc = myClassCons.func(100);
		System.out.println("val in mc is: " + mc.getValue());
		
		/*
		* 数组引用的创建 第一个参数是传入数组长度,第二个参数是传入数组类型, 真正实例化的时候跟  Scala 中 apply 类似来构造
		* 多看源码即可理解思路
		* */
		Function<Integer, String[]> function = (x) -> new String[x];
		Function<Integer, String[]> functionNew = String[]::new;
		String[] abc = function.apply(12);
		System.out.println(function.apply(12).length + functionNew.apply(33).length);
	}

}

挺好的一个例子参考:

package mytest;

import java.util.Arrays;
import java.util.List;

@FunctionalInterface
interface Supplier<T>
{
	T get();
}

class Car
{
	public static Car create(final Supplier<Car> supplier)
	{
		return supplier.get();
	}

	public static void collide(final Car car)
	{
		System.out.println("静态方法形式调用 " + car.toString());
	}

	public void follow(final Car another)
	{
		System.out.println("对象方法形式调用 " + another.toString());
	}

	public void repair()
	{
		System.out.println("任意对象方法引用 " + this.toString());
	}

	@Override
	public String toString()
	{
		return "just a Car " + this.hashCode();
	}
}

public class CRacer
{
	public static void main(String[] args)
	{
		//构造器引用:它的语法是Class::new,或者更一般的Class< T >::new实例如下:
		final Car car = Car.create(Car::new);
		System.out.println(car.hashCode());
		final List<Car> cars = Arrays.asList(car);

		//静态方法引用:它的语法是Class::static_method,实例如下:
		cars.forEach(Car::collide);

		//特定类的任意对象的方法引用:它的语法是Class::method实例如下:
		cars.forEach(Car::repair);
        		cars.forEach(System.out::println); 
		//  forEach 应该接受的要是cars的父类,而我们传入的函数 
          //   println 是 Object 是所以类型的父类 因此可以执行。 再理解下 多态
		 
		//特定对象的方法引用:它的语法是instance::method实例如下:
		final Car police = Car.create(Car::new);
		System.out.println(police.hashCode());
		cars.forEach(police::follow);
	}
}

参考:

  1. https://blog.csdn.net/qq_32403063/article/details/86002379
  2. https://www.jb51.net/article/162534.htm
  3. https://www.cnblogs.com/sunyubin/p/9751412.html
  4. https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html
  5. https://stackoverflow.com/questions/32855138/how-does-a-method-reference-to-an-instance-method-of-an-arbitrary-object-of-a-p
  6. https://stackoverflow.com/questions/25512532/instance-method-reference-and-lambda-parameters
  7. https://segmentfault.com/a/1190000012269548
  8. https://blog.csdn.net/qwe125698420/article/details/53415746
  9. https://blog.csdn.net/learningcoding/article/details/72539918
  10. https://blog.idrsolutions.com/2015/02/java-8-method-references-explained-5-minutes/
  11. java8 默认方法
 
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_31821675/article/details/103180894

智能推荐

什么是BPM工具?BPM工具有哪些功能?白码详解_白码低代码的博客-程序员秘密

BPM的中文名称叫业务流程管理,也就是说BPM工具是对企业业务流程进行管理的工具,其目的就是为了提高企业业绩。很多人可能不知道什么是BPM工具,也不知道BPM工具有什么功能。下面和小编一起来了解一下相关的知识吧!  什么是BPM工具?  业务流程管理(BPM)工具的最终目标是提高公司绩效。它们提供了系统的方法来管理和优化公司的业务流程。BPM工具通常有助于设计,建模,实施和测量工作流和业务规则,从而帮助公司优化和优化涉及人机交互或多个业务应用程序的流程。目标可能是减少效率低下,人为错误或误解。  很多

Android基于GridView实现的翻牌游戏效果_weixin_30828379的博客-程序员秘密

  好久没有写博客了,上一篇博文距现在都有三个多月了,实在是惭愧。但是这段时间仍然是在忙于项目或是自我充电。这几天实现了一个基于GridView的翻牌动画效果,这里就将其整理出来同各位分享。  一、整体介绍   GridView设置15个Item(5*3),刚好满屏显示(没有ScrollBar),没有点击某个Item前可以使用摇一摇功能对GridView中的图片进行切换,点击某个Item时...

性能工具 MiniProfilter_W_wjl1900的博客-程序员秘密

针对于.Net MVC+EF的开发模式,MiniProfilter提供了监控调试EF的功能,可以查看运行了那些sql,sql执行的时间等。一、安装vs打开NuGet的管理NuGet程序包,在线搜索MiniProfilter,如下图 安装完成后,在global文件中加上相应代码using System;using System.Collections.Generic;using System.L

solr第四篇(solr整合数据源)_chenzhi3407的博客-程序员秘密

相关文章:solr第一篇(solr5.5.4下载linux安装)solr第二篇(Solr5.5.4集成Tomcat8部署)solr第三篇(IK分词器安装配置)solr第四篇(solr整合数据源)设置完成第四篇必看#编辑/usr/local/tomcat/solr_home/new_...

【Deep Learning】Logistic Regression with a Neural Network mindset_CrazyCoder1992的博客-程序员秘密

Talk is cheap, speak in code.# coding=utf-8import numpy as npfrom lr_utils import load_dataset# GRADED FUNCTION: sigmoiddef sigmoid(z): """ Compute the sigmoid of z Arguments: ...

[学习笔记]convolutional neural networks_1361976860的博客-程序员秘密

这是andrew ng所开课程convolutional neural networks的学习笔记。我是一名数字工程师,主要工作在于芯片前端设计、fpga开发,由于人工智能芯片含有深度学习加速器,因而对深度学习、机器学习感兴趣,尤其是图像视频相关的人工智能算法。欢迎联系。QQ/微信:526160753facebook: wordchaoconvolutional ...

随便推点

Spring RequestScope和SessionScope的来龙去脉_lan_qie的博客-程序员秘密

Spring在bean配置时可以配置scope(bean的作用域),主要用来控制bean的生命周期,在spring2.0之前bean只有2种作用域即:singleton(单例)、non-singleton(也称prototype), Spring2.0以后,增加了session、request、global session三种专用于Web应用程序上下文的Bean。所以,默认情况下Spring2...

CIFAR10-VGG16_码奋的博客-程序员秘密

Fitting a VGG-16 network on CIFAR-10 for image classification! We use gradient clipping for faster convergence.来源网络 import tensorflow as tf...

FCKEditor的使用-asp_anlei3083的博客-程序员秘密

最近做的一个网站中要用到FCKEditor。网站采用asp开发,所以涉及到FCKEditor在asp下的配置相关的东西。以前也用过这类的HTML在线编辑器,比如eWebEditor等,但是总是每次用每次忘。这次找到了一个比较好的介绍。记录一下。FCKeditor 2.5 使用方法 配置ASP上传功能http://hi.baidu.com/rabeta/blog/item/97...

黑马程序员--08 OC 核心语法_韩胜辉的博客-程序员秘密

#点语法#本质:是方法调用,并不是访问成员变量p.age  =  10;  等价  [p  setAge:10];int a = p.age;  等价 [p age];编译器特性:当使用点语法时,编译器会自动展开称相应的方法。#成员变量作用域#基本概念  :局部变量,全局变量,都有自己的作用域,成员变量也不例外四大类型的成员变量的

[64]python爬虫利器四之PhantomJS的用法_phantomjs useragent_周小董的博客-程序员秘密

前言大家有没有发现之前我们写的爬虫都有一个共性,就是只能爬取单纯的html代码,如果页面是JS渲染的该怎么办呢?如果我们单纯去分析一个个后台的请求,手动去摸索JS渲染的到的一些结果,那简直没天理了。所以,我们需要有一些好用的工具来帮助我们像浏览器一样渲染JS处理的页面。其中有一个比较常用的工具,那就是PhantomJS Full web stack&amp;nbsp;No browser ...

A deep learning approach to segmentation of nasopharyngeal carcinoma using computed tomography_えい川的博客-程序员秘密

读书报告吴欣洋论文基本信息:A deep learning approach to segmentation of nasopharyngeal carcinoma using computed tomographyConceptProblem/challenge/background与MRI相比,CT在低成本和广泛可用性方面具有明显的优势。 但是,CT图像的软组织对比度比MRI图像低得多,这给使用CT进行NPC分割带来了更大的挑战。 数据集小(采集并人工标注)...

推荐文章

热门文章

相关标签