最新文章
JAVA——lambda表达式
lambda表达式是java8的新特性。为什么使用LambdaLambda是一个可传递的代码块,可以在以后传递一次或多次。Lambda可是对一个接口进行快速实现。使用lambda表达式实现接口注意接口必须是一个函数式接口(只有一个抽象方法需要实现的接口)。设计自己的lambda函数式接口可以在接口上加注解@FunctionInterface。lambda基本语法():写参数列表。->:goes to参数方法连接符。{}:写方法体。简单的例子:1、先定义接口@FunctionalInterfacepublic interface LambdaBase { int add(int a, int b);}2、测试代码public class LambdaBaseTest { public static void main(String[] args) { //lambda表达式 LambdaBase lambdaBase = (a, b) -> { return a + b; }; //输出结果 System.out.println(lambdaBase.add(10, 20)); }}3、输出结果为30。lambda语法简化1、准备接口@FunctionalInterfacepublic interface LambdaBase { int add(int a, int b);}@FunctionalInterfacepublic interface LambdaOneParamNoReturn { void test(String a);}2、测试代码public class JianHua { public static void main(String[] args) { //1、参数只有一个可省略小括号 LambdaOneParamNoReturn lambdaOneParamNoReturn = a -> { System.out.println("输出" + a); }; lambdaOneParamNoReturn.test("apple"); //2、如果方法体只有一句,可以省略大括号 LambdaOneParamNoReturn lambdaOneParamNoReturn1 = a -> System.out.println("输出" + a); lambdaOneParamNoReturn1.test("abc"); //3、如果方法体的唯一一句是返回语句,在省略大括号的同时要省略return关键字 LambdaBase lambdaBase = (a, b) -> a + b; System.out.println(lambdaBase.add(1, 2)); }}方法引用lambda方法引用是lambda指向的是一个具体的方法。语法:方法隶属者::方法名。(方法隶属者是对象名或类名)。注意:方法的参数列表、返回值必须与接口完全一样。示例如下1、接口@FunctionalInterfacepublic interface LambdaBase { int add(int a, int b);}2、测试public class FangFaYinYong { public static void main(String[] args) { LambdaBase lambdaBase = FangFaYinYong::add; System.out.println(lambdaBase.add(1, 2)); } public static int add(int a, int b) { return a + b; }}输出结果3。构造器引用语法:构造的对象::new示例:1、对象public class Student { private String name; private Integer age; public Student() { System.out.println("无参构造器执行了"); } public Student(String name, Integer age) { this.name = name; this.age = age; System.out.println("有参构造器执行了"); }} 2、接口/** * 无参数接口 */@FunctionalInterfacepublic interface StudentNoParamConstruct { Student getStudent();}/** * 有参接口 */@FunctionalInterfacepublic interface StudentHaveParamConstruct { Student getStudent(String name, int age);}3、测试public class ConstructorTest { public static void main(String[] args) { //无参构造器引用 StudentNoParamConstruct studentNoParamConstruct = Student::new; studentNoParamConstruct.getStudent(); //有参构造器引用 StudentHaveParamConstruct studentHaveParamConstruct = Student::new; studentHaveParamConstruct.getStudent("jack", 20); }}打印结果:无参构造器执行了有参构造器执行了forEach遍历集合语法:集合.forEach(lambda表达式)。示例public static void main(String[] args) { List<Integer> list = new ArrayList<>(); Collections.addAll(list, 1, 2, 3, 4, 5, 6); //打印集合元素 list.forEach(System.out::println); //处理集合元素(输出大于5的元素) list.forEach(ele -> { if (ele > 5) { System.out.println(ele); } });}removeIf作用:删除集合中符合条件的元素。示例public static void main(String[] args) { List<Integer> list = new ArrayList<>(); Collections.addAll(list, 1, 2, 3, 4, 5, 6); //删除奇数 list.removeIf(elt -> elt % 2 != 0); System.out.println(list);}打印结果:[2, 4, 6]线程实现示例public static void main(String[] args) { //用lambda实例化线程 java.lang.Thread thread = new java.lang.Thread(() -> { for (int i = 0; i < 10; i++) { System.out.println(i); } }); thread.start();}java常用函数式接口java常用函数式接口参考:https://www.runoob.com/java/java8-functional-interfaces.html闭包闭包会提升包围变量的生命周期(在方法结束后变量不会销毁)。在使用lambda表达式时,在闭包中引用的变量一定是常量,举例说明:public static void main(String[] args) { int a = 5; Consumer<Integer> consumer = elt -> System.out.println(a++); //a++编译出错:Variable used in lambda expression should be final or effectively final consumer.accept(1);}原因:只要在闭包中使用了某个局部变量值,就会将这个局部变量用final修饰,变量值不可更改。像上面这个例子变量a在编译时会以final int a = 5来编译。
Apr 27, 2019 8:13:50 PM
692
JAVA——对象数组排序
Comparable接口Arrays类中的sort方法会对对象数组进行排序,但前提是对象所属的类必须实现了Comparable接口。例:使用Comparable接口对学生对象数组排序(按学生年龄排序)public class ComparableStudent { public static void main(String[] args) { //构造三个学生对象放入数组 Student student1 = new Student("小明",12); Student student2 = new Student("小红",11); Student student3 = new Student("小刚",14); Student[] students = {student1,student2,student3}; System.out.println("排序前:\n" + Arrays.toString(students)); //排序 Arrays.sort(students); System.out.println("排序后:\n" + Arrays.toString(students)); }}class Student implements Comparable<Student> { private String name; private Integer age; public Student(String name, Integer age) { this.name = name; this.age = age; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } @Override public int compareTo(Student o) { return Integer.compare(age, o.age); }}打印结果:排序前:[Student{name='小明', age=12}, Student{name='小红', age=11}, Student{name='小刚', age=14}]排序后:[Student{name='小红', age=11}, Student{name='小明', age=12}, Student{name='小刚', age=14}]Comparator接口Arrays.sort()还有第二种用法,就是一个数组和比较器作为参数,比较器必须实现Comparator接口。例:使用Comparator接口对学生对象数组排序(按学生年龄排序)public class ComparatorStudent { public static void main(String[] args) { //构造三个学生对象放入数组 Student1 student1 = new Student1("小明", 12); Student1 student2 = new Student1("小红", 11); Student1 student3 = new Student1("小刚", 14); Student1[] students = {student1, student2, student3}; System.out.println("排序前:\n" + Arrays.toString(students)); //排序 Arrays.sort(students, new AgeComparator()); System.out.println("排序后:\n" + Arrays.toString(students)); }}/** * 学生类 */class Student1 { private String name; private Integer age; public Student1(String name, Integer age) { this.name = name; this.age = age; } @Override public String toString() { return "Student1{" + "name='" + name + '\'' + ", age=" + age + '}'; } public String getName() { return name; } public Integer getAge() { return age; }}/** * 比较器 */class AgeComparator implements Comparator<Student1> { @Override public int compare(Student1 o1, Student1 o2) { return o1.getAge() - o2.getAge(); }}打印结果:排序前:[Student1{name='小明', age=12}, Student1{name='小红', age=11}, Student1{name='小刚', age=14}]排序后:[Student1{name='小红', age=11}, Student1{name='小明', age=12}, Student1{name='小刚', age=14}]lambda表达式java8的lambda表达式可以简单快速地实现用比较器的方式对对象数组排序。例:使用lambda表达式接口对学生对象数组排序(按学生年龄排序)public class LambdaStudent { public static void main(String[] args) { //构造三个学生对象放入数组 Student2 student1 = new Student2("小明", 12); Student2 student2 = new Student2("小红", 11); Student2 student3 = new Student2("小刚", 14); Student2[] students = {student1, student2, student3}; System.out.println("排序前:\n" + Arrays.toString(students)); //排序 Arrays.sort(students, (s1, s2) -> s1.getAge() - s2.getAge()); System.out.println("排序后:\n" + Arrays.toString(students)); }}class Student2 { private String name; private Integer age; public Student2(String name, Integer age) { this.name = name; this.age = age; } @Override public String toString() { return "Student2{" + "name='" + name + '\'' + ", age=" + age + '}'; } public String getName() { return name; } public Integer getAge() { return age; }}打印结果:排序前:[Student1{name='小明', age=12}, Student1{name='小红', age=11}, Student1{name='小刚', age=14}]排序后:[Student1{name='小红', age=11}, Student1{name='小明', age=12}, Student1{name='小刚', age=14}]
Apr 1, 2019 9:10:46 PM
719
JAVA——反射
反射能够分析类能力的程序成为反射。Class每一个类是Class类的实例对象。获取类类型的方法类名.class == 实例对象.getClass() == Class.forName(“类路径”)。当一个类存在无参构造器可以获取类类型,使用“类类型.newInstance()”实例该类对象。基本数据类型像基本诗句类型int、void关键字都有自己的类类型。动态加载反射可以实现动态加载类(程序运行时加载类)。通过API获取类信息获取类方法信息/** * 打印类信息 * @param object 对象 */public static void printClassInfo(Object object){ //先要获取类类型 Class classType = object.getClass(); //getName()包含类路径获取类名称,getSimpleName()获取不包含类路径的的方法名 System.out.println("类名:" + classType.getName()); /** * getMethods():获取所有public方法信息,包括从父类继承而来的 * getDeclaredMethods():获取该类自声明的方法,不论访问权限。 */ System.out.println("方法:"); Method[] methods = classType.getMethods(); for (int i = 0; i < methods.length; i++) { //获取方法返回值类类型 Class returnType = methods[i].getReturnType(); //获取返回值类型 System.out.print(returnType.getName() + " "); //获取方法名 System.out.print(methods[i].getName() + "("); //获取方法所有参数的类类型 Class[] params = methods[i].getParameterTypes(); for (Class param : params) { //获取参数类型 System.out.print(param.getName() + ","); } System.out.print(")\n"); }}public static void main(String[] args) { //如获取String对象的类信息 APITest.printClassInfo("test");}打印结果:类名:java.lang.String方法:boolean equals(java.lang.Object,)java.lang.String toString()int hashCode()......获取成员变量信息/** * 获取成员变量 * @param object */public static void printFields(Object object){ //先要获取类类型 Class classType = object.getClass(); /** * getFields()获取所有public成员变量信息 * getDeclaredFields()获取所有自定义成员变量信息 */ //Field[] fields = classType.getFields(); Field[] fields = classType.getDeclaredFields(); for (Field field : fields) { //获取成员变量类类型 Class fieldType = field.getClass(); System.out.print(fieldType.getName() + " "); //成员变量名称 System.out.print(field.getName() + "\n"); }}public static void main(String[] args) { //如获取String对象的类信息 //APITest.printClassInfo("test"); APITest.printFields("test");}打印结果:java.lang.reflect.Field valuejava.lang.reflect.Field hashjava.lang.reflect.Field serialVersionUIDjava.lang.reflect.Field serialPersistentFieldsjava.lang.reflect.Field CASE_INSENSITIVE_ORDER获取构造方法/** * 获取构造方法 * @param object */public static void printConstruct(Object object){ //先要获取类类型 Class classType = object.getClass(); /** * getConstructors()获取所有public构造方法信息 * getDeclaredConstructors()获取所有自定义构造方法信息 */ Constructor[] constructors = classType.getDeclaredConstructors(); for (Constructor constructor : constructors) { //获取方法名 System.out.print(constructor.getName() + "("); //获取参数类类型 Class[] paramType = constructor.getParameterTypes(); for (Class aClass : paramType) { System.out.print(aClass.getName() + ","); } System.out.print(constructor.getName() + ")\n"); }}public static void main(String[] args) { APITest.printConstruct("test");}打印结果:java.lang.String([B,int,int,java.lang.String)java.lang.String([B,java.nio.charset.Charset,java.lang.String)java.lang.String([B,java.lang.String,java.lang.String)java.lang.String([B,int,int,java.nio.charset.Charset,java.lang.String)...... 备注:其他反射相关方法查看API。方法的反射一个方法有方法名称和参数决定。method.invoke(对象,参数列表);如果操作的方法没有返回值返回null,否则返回对应的值。public class MethodReflectTest { public static void main(String[] args) { //获取testA的类类型 TestA testA = new TestA(); Class aType = testA.getClass(); /** * 操作方法A */ try { //获取方法 Method methodA = aType.getMethod("methodA", int.class, int.class); //调用方法 System.out.println("方法A返回结果:" + methodA.invoke(testA, 10, 5)); } catch (Exception e) { e.printStackTrace(); } System.out.println("======================================================="); /** * 操作方法B */ try { //获取方法 Method methodB = aType.getMethod("methodB", new Class[]{int.class, int.class}); //调用方法 System.out.println("方法B返回结果:" + methodB.invoke(testA, new Object[]{10, 5})); } catch (Exception e) { e.printStackTrace(); } }}class TestA { public void methodA(int a, int b) { System.out.println(a + b); } public int methodB(int a, int b) { return a - b; }}打印结果:15方法A返回结果:null=======================================================方法B返回结果:5集合的泛型集合的泛型只是在编译阶段用来防止用户输入错误,编译之后集合是去泛型的。绕开编译,向集合加不同类型的数据public static void main(String[] args) { //定义一个泛型为String的list集合 List<String> list = new ArrayList<>(); //加入一个元素 list.add("a"); System.out.println(list); //获取list的类类型 Class listType = list.getClass(); try { //获取add方法 Method methodAdd = listType.getMethod("add", Object.class); //通过反射向list加入一个int类型的数据 methodAdd.invoke(list, 10); } catch (Exception e) { e.printStackTrace(); } System.out.println(list);}打印结果:[a][a, 10]
Mar 31, 2019 4:41:34 PM
695
SPRINGBOOT之QQ登录
最近在网站上做了一个QQ登录OAuth2.0接入的功能,下面我就来分享一下如何实现QQ登录。首先在开发之前我们需要为QQ登录做如下准备:1、登录QQ互联https://connect.qq.com/成为开发者(成为开发者需要填写资料,审核时间大概2天吧)。2、有了开发者账号之后就能创建应用,这时会要求你填写网站信息,之后又会进入审核阶段(本人当时几个小时就审核过了)。接下来就是开发阶段了根据QQ互联官方网站可以知道QQ登录开发流程如下:1、获取appid和appkey,appid和appkey在你创建的应用信息中查找。2、放置QQ登录按钮。3、获取Authorization Code。4、通过Authorization Code获取Access Token。5、获取QQ用户的openid。6、通过openid访问/修改QQ用户信息。以下为开发步骤和示例代码本项目是一个maven构建的项目。使用fastjson处理json数据,fastjson依赖:<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.47</version></dependency>一、写好配置功能1、配置基本信息## 第三方登录配置信息oauth: qq: app_id: ---------------- app_key: -------------------------------- # 获取ocde地址 get_authorization_code_uri: https://graph.qq.com/oauth2.0/authorize # 获取ocde的参数,固定为code response_type: code # 回调地址 redirect_uri: --------------------------- # 获取Access Token地址 get_access_token_uri: https://graph.qq.com/oauth2.0/token # 获取Access Token,固定为authorization_code token_grant_type: authorization_code # 刷新Access Token,refresh_token refresh_token_grant_type: refresh_token # 获取openid的地址 get_openid_uri: https://graph.qq.com/oauth2.0/me # 获取用户信息地址 get_user_info_uri: https://graph.qq.com/user/get_user_info 2、编写配置信息的配置类,通过该配置类获取配置信息@Component@ConfigurationProperties(prefix = "oauth.qq")public class QqOAuthConfig { private String appId; private String appKey; /** * 获取ocde地址 */ private String getAuthorizationCodeUri; /** * 获取ocde的参数,固定为code */ private String responseType; /** * 回调地址 */ private String redirectUri; /** * 获取Access Token地址 */ private String getAccessTokenUri; /** * 获取Access Token,固定为authorization_code */ private String tokenGrantType; /** * 刷新Access Token,refresh_token */ private String refreshTokenGrantType; /** * 获取openid的地址 */ private String getOpenidUri; /** * 获取用户信息地址 */ private String getUserInfoUri; public String getAppId() { return appId; } public void setAppId(String appId) { this.appId = appId; } public String getAppKey() { return appKey; } public void setAppKey(String appKey) { this.appKey = appKey; } public String getGetAuthorizationCodeUri() { return getAuthorizationCodeUri; } public void setGetAuthorizationCodeUri(String getAuthorizationCodeUri) { this.getAuthorizationCodeUri = getAuthorizationCodeUri; } public String getResponseType() { return responseType; } public void setResponseType(String responseType) { this.responseType = responseType; } public String getRedirectUri() { return redirectUri; } public void setRedirectUri(String redirectUri) { this.redirectUri = redirectUri; } public String getGetAccessTokenUri() { return getAccessTokenUri; } public void setGetAccessTokenUri(String getAccessTokenUri) { this.getAccessTokenUri = getAccessTokenUri; } public String getTokenGrantType() { return tokenGrantType; } public void setTokenGrantType(String tokenGrantType) { this.tokenGrantType = tokenGrantType; } public String getRefreshTokenGrantType() { return refreshTokenGrantType; } public void setRefreshTokenGrantType(String refreshTokenGrantType) { this.refreshTokenGrantType = refreshTokenGrantType; } public String getGetOpenidUri() { return getOpenidUri; } public void setGetOpenidUri(String getOpenidUri) { this.getOpenidUri = getOpenidUri; } public String getGetUserInfoUri() { return getUserInfoUri; } public void setGetUserInfoUri(String getUserInfoUri) { this.getUserInfoUri = getUserInfoUri; }} 二、放置QQ登录按钮三、接下来就是QQ登录主要流程。此时我们需要httpclient工具来获取通过请求返回的信息,小伙伴们可以去百度了解一下httpclient。1、加入httpclient依赖<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.7</version></dependency> 2、配置httpclient工具1)封装响应结果public class HttpClientResult implements Serializable { /** * 响应状态码 */ private int code; /** * 响应数据 */ private String content; public HttpClientResult() { } public HttpClientResult(int code) { this.code = code; } public HttpClientResult(int code, String content) { this.code = code; this.content = content; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getContent() { return content; } public void setContent(String content) { this.content = content; }} 2)封装httpClient工具类public class HttpClientUtil { // 编码格式。发送编码格式统一用UTF-8 private static final String ENCODING = "UTF-8"; // 设置连接超时时间,单位毫秒。 private static final int CONNECT_TIMEOUT = 6000; // 请求获取数据的超时时间(即响应时间),单位毫秒。 private static final int SOCKET_TIMEOUT = 6000; /** * 发送get请求;不带请求头和请求参数 * * @param url 请求地址 * @return * @throws Exception */ public static HttpClientResult doGet(String url) throws Exception { return doGet(url, null, null); } /** * 发送get请求;带请求参数 * * @param url 请求地址 * @param params 请求参数集合 * @return * @throws Exception */ public static HttpClientResult doGet(String url, Map<String, String> params) throws Exception { return doGet(url, null, params); } /** * 发送get请求;带请求头和请求参数 * * @param url 请求地址 * @param headers 请求头集合 * @param params 请求参数集合 * @return * @throws Exception */ public static HttpClientResult doGet(String url, Map<String, String> headers, Map<String, String> params) throws Exception { // 创建httpClient对象 CloseableHttpClient httpClient = HttpClients.createDefault(); // 创建访问的地址 URIBuilder uriBuilder = new URIBuilder(url); if (params != null) { Set<Map.Entry<String, String>> entrySet = params.entrySet(); for (Map.Entry<String, String> entry : entrySet) { uriBuilder.setParameter(entry.getKey(), entry.getValue()); } } // 创建http对象 HttpGet httpGet = new HttpGet(uriBuilder.build()); /** * setConnectTimeout:设置连接超时时间,单位毫秒。 * setConnectionRequestTimeout:设置从connect Manager(连接池)获取Connection * 超时时间,单位毫秒。这个属性是新加的属性,因为目前版本是可以共享连接池的。 * setSocketTimeout:请求获取数据的超时时间(即响应时间),单位毫秒。 如果访问一个接口,多少时间内无法返回数据,就直接放弃此次调用。 */ RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build(); httpGet.setConfig(requestConfig); // 设置请求头 packageHeader(headers, httpGet); // 创建httpResponse对象 CloseableHttpResponse httpResponse = null; try { // 执行请求并获得响应结果 return getHttpClientResult(httpResponse, httpClient, httpGet); } finally { // 释放资源 release(httpResponse, httpClient); } } /** * 发送post请求;不带请求头和请求参数 * * @param url 请求地址 * @return * @throws Exception */ public static HttpClientResult doPost(String url) throws Exception { return doPost(url, null, null); } /** * 发送post请求;带请求参数 * * @param url 请求地址 * @param params 参数集合 * @return * @throws Exception */ public static HttpClientResult doPost(String url, Map<String, String> params) throws Exception { return doPost(url, null, params); } /** * 发送post请求;带请求头和请求参数 * * @param url 请求地址 * @param headers 请求头集合 * @param params 请求参数集合 * @return * @throws Exception */ public static HttpClientResult doPost(String url, Map<String, String> headers, Map<String, String> params) throws Exception { // 创建httpClient对象 CloseableHttpClient httpClient = HttpClients.createDefault(); // 创建http对象 HttpPost httpPost = new HttpPost(url); /** * setConnectTimeout:设置连接超时时间,单位毫秒。 * setConnectionRequestTimeout:设置从connect Manager(连接池)获取Connection * 超时时间,单位毫秒。这个属性是新加的属性,因为目前版本是可以共享连接池的。 * setSocketTimeout:请求获取数据的超时时间(即响应时间),单位毫秒。 如果访问一个接口,多少时间内无法返回数据,就直接放弃此次调用。 */ RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build(); httpPost.setConfig(requestConfig); // 设置请求头 /*httpPost.setHeader("Cookie", ""); httpPost.setHeader("Connection", "keep-alive"); httpPost.setHeader("Accept", "application/json"); httpPost.setHeader("Accept-Language", "zh-CN,zh;q=0.9"); httpPost.setHeader("Accept-Encoding", "gzip, deflate, br"); httpPost.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36");*/ packageHeader(headers, httpPost); // 封装请求参数 packageParam(params, httpPost); // 创建httpResponse对象 CloseableHttpResponse httpResponse = null; try { // 执行请求并获得响应结果 return getHttpClientResult(httpResponse, httpClient, httpPost); } finally { // 释放资源 release(httpResponse, httpClient); } } /** * 发送put请求;不带请求参数 * * @param url 请求地址 * @param * @return * @throws Exception */ public static HttpClientResult doPut(String url) throws Exception { return doPut(url); } /** * 发送put请求;带请求参数 * * @param url 请求地址 * @param params 参数集合 * @return * @throws Exception */ public static HttpClientResult doPut(String url, Map<String, String> params) throws Exception { CloseableHttpClient httpClient = HttpClients.createDefault(); HttpPut httpPut = new HttpPut(url); RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build(); httpPut.setConfig(requestConfig); packageParam(params, httpPut); CloseableHttpResponse httpResponse = null; try { return getHttpClientResult(httpResponse, httpClient, httpPut); } finally { release(httpResponse, httpClient); } } /** * 发送delete请求;不带请求参数 * * @param url 请求地址 * @param * @return * @throws Exception */ public static HttpClientResult doDelete(String url) throws Exception { CloseableHttpClient httpClient = HttpClients.createDefault(); HttpDelete httpDelete = new HttpDelete(url); RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build(); httpDelete.setConfig(requestConfig); CloseableHttpResponse httpResponse = null; try { return getHttpClientResult(httpResponse, httpClient, httpDelete); } finally { release(httpResponse, httpClient); } } /** * 发送delete请求;带请求参数 * * @param url 请求地址 * @param params 参数集合 * @return * @throws Exception */ public static HttpClientResult doDelete(String url, Map<String, String> params) throws Exception { if (params == null) { params = new HashMap<String, String>(); } params.put("_method", "delete"); return doPost(url, params); } /** * Description: 封装请求头 * * @param params * @param httpMethod */ public static void packageHeader(Map<String, String> params, HttpRequestBase httpMethod) { // 封装请求头 if (params != null) { Set<Map.Entry<String, String>> entrySet = params.entrySet(); for (Map.Entry<String, String> entry : entrySet) { // 设置到请求头到HttpRequestBase对象中 httpMethod.setHeader(entry.getKey(), entry.getValue()); } } } /** * Description: 封装请求参数 * * @param params * @param httpMethod * @throws UnsupportedEncodingException */ public static void packageParam(Map<String, String> params, HttpEntityEnclosingRequestBase httpMethod) throws UnsupportedEncodingException { // 封装请求参数 if (params != null) { List<NameValuePair> nvps = new ArrayList<NameValuePair>(); Set<Map.Entry<String, String>> entrySet = params.entrySet(); for (Map.Entry<String, String> entry : entrySet) { nvps.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); } // 设置到请求的http对象中 httpMethod.setEntity(new UrlEncodedFormEntity(nvps, ENCODING)); } } /** * Description: 获得响应结果 * * @param httpResponse * @param httpClient * @param httpMethod * @return * @throws Exception */ public static HttpClientResult getHttpClientResult(CloseableHttpResponse httpResponse,CloseableHttpClient httpClient, HttpRequestBase httpMethod) throws Exception { // 执行请求 httpResponse = httpClient.execute(httpMethod); // 获取返回结果 if (httpResponse != null && httpResponse.getStatusLine() != null) { String content = ""; if (httpResponse.getEntity() != null) { content = EntityUtils.toString(httpResponse.getEntity(), ENCODING); } return new HttpClientResult(httpResponse.getStatusLine().getStatusCode(), content); } return new HttpClientResult(HttpStatus.SC_INTERNAL_SERVER_ERROR); } /** * Description: 释放资源 * * @param httpResponse * @param httpClient * @throws IOException */ public static void release(CloseableHttpResponse httpResponse, CloseableHttpClient httpClient) throws IOException { // 释放资源 if (httpResponse != null) { httpResponse.close(); } if (httpClient != null) { httpClient.close(); } }} 3、点击QQ登录的按钮获取会跳转到QQ登录页面。如果QQ授权登录成功将会跳转到指定的回调地址(创建应用是填写的地址),获取到Authorization Code,该code有效期10分钟。@GetMapping("/login/qq")public void loginQQ(HttpServletResponse httpServletResponse, String thirdLoginBackHref) { try { httpServletResponse.sendRedirect(qqOAuthConfig.getGetAuthorizationCodeUri() + "?response_type=" + qqOAuthConfig.getResponseType() + "&client_id=" + qqOAuthConfig.getAppId() + "&redirect_uri=" + qqOAuthConfig.getRedirectUri() + "&state=" + UuidUtil.getUuid()); } catch (Exception e) { throw new BaseException("请求参数异常"); }} 1、编写回调地址。通过Authorization Code获取Access Token(2小时有效期);获取QQ用户的openid;通过openid访问QQ用户信息。1)回调controller@GetMapping("/login/qq/callback")public String qqCallbackUri(String code) { loginService.qqLogin(code); return "/"; //返回地址} 2)回调service,获取用户信息。@Override@Transactionalpublic void qqLogin(String code) { //获取access_token的请求参数 HashMap<String, String> getTokenParams = new HashMap<>(); getTokenParams.put("grant_type", qqOAuthConfig.getTokenGrantType()); getTokenParams.put("client_id", qqOAuthConfig.getAppId()); getTokenParams.put("client_secret", qqOAuthConfig.getAppKey()); getTokenParams.put("code", code); getTokenParams.put("redirect_uri", qqOAuthConfig.getRedirectUri()); //获取access_token HttpClientResult resultOfGetAcccessToken = null; try { resultOfGetAcccessToken = HttpClientUtil.doGet(qqOAuthConfig.getGetAccessTokenUri(), getTokenParams); } catch (Exception e) { throw new BaseException("QQ登录异常"); } //获取access_token返回参数 String resultParamsOfGetToken = resultOfGetAcccessToken.getContent(); if (resultOfGetAcccessToken.getCode() != 200 || (resultParamsOfGetToken == null || resultParamsOfGetToken.equals(""))) { throw new BaseException("QQ登录异常"); } //处理获取access_token返回参数成json String[] tokenResultString = resultParamsOfGetToken.split("&"); Map<String, String> tokenResultMap = new HashMap<>(); for (String s : tokenResultString) { String keyValue[] = s.split("="); if (keyValue.length > 1) { tokenResultMap.put(keyValue[0], keyValue[1]); } } //access_token String accessToken = tokenResultMap.get("access_token"); //获取openid HttpClientResult resultOfGetOpenid = null; try { resultOfGetOpenid = HttpClientUtil.doGet(qqOAuthConfig.getGetOpenidUri() + "?access_token=" + accessToken); } catch (Exception e) { throw new BaseException("QQ登录异常"); } //处理获取openid结果 String resultOfOpenidContent = resultOfGetOpenid.getContent(); if (resultOfGetOpenid.getCode() != 200 || (resultOfOpenidContent == null || resultOfOpenidContent.equals(""))) { throw new BaseException("QQ登录异常"); } JSONObject resultByGetOpenIdJson = JSON.parseObject(resultOfOpenidContent.replaceAll("callback\\( | \\);", "").trim()); //openid String openid = (String) resultByGetOpenIdJson.get("openid"); if (resultByGetOpenIdJson.get("client_id") == null || openid == null) { throw new BaseException("QQ登录异常"); } //获取用户信息 HttpClientResult resultByGetUserInfo = null; try { resultByGetUserInfo = HttpClientUtil.doGet(qqOAuthConfig.getGetUserInfoUri() + "?access_token=" + accessToken + "&oauth_consumer_key=" + qqOAuthConfig.getAppId() + "&openid=" + openid); } catch (Exception e) { throw new BaseException("QQ登录异常"); } //处理用户数据 JSONObject qqUserInfoJson = JSON.parseObject(resultByGetUserInfo.getContent()); //qq头像 String chatHead = (String) qqUserInfoJson.get("figureurl_qq_1"); //qq昵称 String nickname = (String) qqUserInfoJson.get("nickname"); //以下便是登录逻辑,这里就省略不多写了,大家可以根据自己的需求编写登录逻辑。} 说明:上面代码最后的变量qqUserInfoJson便是一个包含了QQ用户信息的json对象,我们可以将自己需要的用户信息取出来,必要时保存在数据库。 虽然省略的登录部分的代码,但在这里也简要说一下QQ登录逻辑:首先QQ登录对于自己的项目肯定是免密登录;其次openid是每个QQ用户在本网站的唯一标识,所以我们QQ登录的用户表一般会存每个用户的openid,在获取到QQ用户信息时判断数据库是否存在与当前openid相同的用户数据,如果没有便添加一条与当前openid相同的用户信息然后登录,如果存在,可以选择直接进入登录逻辑或修改相关登录信息后进入登录逻辑。 这里只介绍简单的QQ登录流程,并没有持续去请求腾讯API获取相关信息,所以刷新access_token功能以后再加入。 至此QQ登录就分享完了,感谢大家的支持!
Mar 20, 2019 9:40:40 PM
808
JAVA核心技术基础学习笔记
java概述java“白皮书”关键术语1. 简单性、面向对象、分布式、健壮性、安全性、体系结构中立、可移植性、解释性、高性能、多线程、动态性。2. 在网页中运行的java程序叫applet。java基础程序设计结构数据类型1. 整型int(4字节 32位)、short(2字节 16位)、long(8字节 64位)、byte(1字节 8位)。long有一个后缀L或l,(如4000000000L);十六进制数值有前缀0x或0X(如0xCAFE);八进制有前缀0(如010代表8),易混淆,不建议使用八进制。1. 浮点型float(4字节)、double(8字节)。float的数值有一个后缀F或f(如3.12f);如果3.12没有后缀默认为double型,double型也可加后缀D或d。三个表溢出或出错的浮点型:正无穷大,负无穷大,NaN。如果在数值计算中不允许任何舍入误差,就用BigDecimal类。Double.isNaN(x)//判断不是数字。1. char型有些Unicode字符用char值描述,另些Unicode字符需要两个char表示。码点:是指一个编码表中的某个字符对应的代码值。代码单元:在基本的多语言级别中,每个字符用16位表示,称为代码单元;辅助字符采用一对连续的代码单元。2. boolean型整型和boolean之间不能相互转换。变量常量利用关键字final指示常量,习惯上常量名全大写。(如final double NUM = 2.23)。运算符 API: Math类运算符:+、-、*、/、等数学函数与常量在Math类中,提供了三角函数,指示函数,π等各式各样的数学函数和常量(如开方Math.sqrt(x)、Math.PI)。数值之间的类型转换如果两个数中有一个double,另一个就会转为double;否则其中一个为float,另一个转为float;否则其中一个为long,另一个转为long;否则另个都将转为int。括号与运算级别+=是右结合运算,所以表达式a += b += c等价于a += (b += c) 字符串API:java.lang.String、java.lang.StringBuffer、java.lang.StringBuilder拼接 用指定符号将多个字符串拼接成新字符串用静态的join()方法。如:String all = String.join("/","a","cc","s");不可变字符串优点:编译器可让字符串共享。构建字符串 StringBuilder(效率高,线程不安全)前身是StringBuffer(线程安全)。控制流程块作用域不能在嵌套的两个块声明相同变量名,如下:{int n;...{int n;...}}条件语句if(condition){statement...} if(condition){statement....}else{statement...} if(condition){statement....}else if(condition){statement...}else{statement...}循环while(condition){statement...} do{statement...}while(condetion);多重选择switch(param){case v1:...break;case v2:...break;case v3:...break;default:...break;}注意:case标签可以是:1. 类型为char、byte、short、int的常量表达式。2. 枚举常量。3. java SE7开始也可以是字符串字面量。大数值API:java.math.BigInteger、java.math.BigDecimal如果基本的整数和浮点数精度不能满足要求,可使用java.math包的BigInter和BigDecimal(如金额)。使用静态的valueOf方法可以将普通数值转为大数值(如BigInteger a = BigInteger.valueOf(100);)。大数值不能用算术运算符(+、-、*、/等),必须使用大数值类中的方法处理大数值(如一个加法运算: BigInteger c = a.add(b);等价于c = a + b;)。数组API:java.util.Arraysfor each循环for(variable : conllection) {statement...}数组拷贝Arrays.copyOf(oldArray,oldArray.length);快速打印数组1. Arrays.toString(a)2. 二维数组:Arrays.deepToString(a);对象与类OOP概述类类是构造对象的模板和蓝图。封装:实现封装的关键在于决不让类中的方法直接访问其他类的实例域(对象中的数据)。程序仅通过对象的方法与对象数据之间进行交互。对象对象的三个特性:1. 行为:可调用的方法。2. 状态:只能通过调用方法来改变。3. 标识:每个对象都不同,状态也常常不一样。类与类之间的关系 1. 依赖:A类的方法操纵B类的对象,A类依赖于B类。 2. 聚合:A对象包含B对象。 3. 继承:A类扩展B类,并且A类添加自己的额外功能。预定义类API:java.time.LocalDate对象与对象变量使用构造器构造一个新对象,关键字new。任何对象变量的值都是对存储在另一个地方的一个对象的引用。new操作的返回值是一个引用。一个方法不能作用于一个值为null的对象上。局部变量不会自动初始化为null,必须通过new或设置为null。LocalDate类Date类:表时间点。LocalDate类:日历表示法,用于维护日期;使用静态工厂方法代表调用构造器,而不是new关键字(如:LocalDate.now();//表构造这个对象的日期)。用户自定义类源(类)文件名必须与public类名字相匹配;在一个源文件中,只能有一个公共类,但可以有任意多的非公共类。构造器构造器与类同名,不要在构造器中定义与实例域同名的变量。隐式参数与显式参数1. 隐式参数:出现在方法名前的类对象(如User.add(x)中的User对象)。在每个方法中this表示隐式参数。2. 显式参数:方法名后括号中的值为显式参数(如User.add(x)中的x)。封装的优点封装一般需要三项类容:1. 一个私有的数据域。2. 一个共有的域访问器方法。3. 一个共有的与更改器方法。优点:数据域改变内部实现,不会影响其他类代码。更改器方法可以检验数据域的合法性。注意:不要编写返回引用可变对象的访问构造器;解决,先对可变数据域(可变对象)克隆再返回。私有方法一个计算代码划分成若干独立的辅助方法,这些辅助方法可设为私有方法。这是由于他们往往与当前实现机制非常紧密,或需要一个特别的协议以及一个特别的调用次序。静态域与静态方法静态域如果将域定义为static,每个类中只能有一个这样的域。它属于类,不属于任何独立对象。静态常量一般设为公有。静态方法静态方法不能向对象实施操作方法,即没有隐式对象或没有this参数的方法。静态方法可以访问自身的静态域。可以通过类名和对象调用。什么时候使用静态方法:1. 一个方法不需要访问对象状态,所需参数都是显式参数提供。2. 一个方法只需访问类的静态域。工厂方法可以使用静态工厂方法构造对象为什么有时不用构造器实例:3. 无法命名构造器(构造器名与类名不同)。4. 当使用构造器时,不能够改变所构造的对象类型。main方法 main方法将执行并创建所需对象方法参数方法得到的是对象引用的拷贝,对象引用及其他拷贝同时引用同一个对象。java对对象采用的不是引用调用,对象引用是按值传递的。对象构造重载返回类型不是方法签名的一部分。即不能有两个名字相同、参数类型也相同却返回值不同类型值的方法。无参构造器无参构造器将所有实例域设为默认值。如果类中至少有一个有参构造器,却没有无参构造器,则不能使用无参构造器实例对象。显式域初始化显式域初始化值不一定是常量,也可以是调用一个方法。调用另一个构造器关键字this不仅是引用方法的隐式参数;还可以通过this(...)调用同一个类的另一个构造器。这样可以对公共的构造器代码只写一次。初始化块只要构造类对象,这些块就会被执行。即先运行初始化块,再运行构造器主体部分。 初始化数据域的方法:1. 在构造器中设置。2. 在声明中赋值。3. 在初始化块中设置。可以提供静态域的初始值;但如果对类的静态域初始化代码比较复杂,也可以使用一个静态初始化块(static{})初始化。对象析构与finalize方法java有自动的垃圾回收器,不需人工回收内存,所以java不支持析构器。如果某个资源需要在使用完毕后立刻关闭,那么就需要人工来管理。对象用完时,可以应用一个close方法来完成相应的清理操作。finalize方法将在垃圾回收器清除对象之前调用。包java允许用包将类组织起来。所有标准的java包都处于java和javax包层次中。类的导入一个类可以使用所属包中的所有类,以及其他包中的公有类。静态导入import不仅可以导入类,还增加了导入静态方法和静态域的功能。包作用域如果没有指定public或private,这个部分(类、方法、或变量)可以被同一个包中的所有方法访问。类路径类路径:类路径是指所有包含类文件的路径集合。编译器任务:1. 定位文件(如定位一个类导入的其他类的类文件)。2. 查看源文件是否比类文件新,如果新,源文件会自动重新编译。类设计技巧1. 一定要保证数据私有。2. 不要在类中使用过多的基本类型。3. 不是所有的域都需要域访问器或域更改器。4. 将职责过多的类进行分解。5. 类和方法名要能体现他们的职责。6. 有限使用不可变的类。类、超类、子类子类构造器如果子类构造器没有显式的调用超类构造器,将自动调用超类默认构造器。如果超类没有不带参数的构造器,并且在子类构造器有没有显式的调用超类的其他构造器,将编译报错。super关键字作用:1. 调用超类方法。2. 调用超类的构造器。 调用构造器的语句只能出现在另一个构造器的第一句。一个对象变量可以指示多种实际类型的现象称为多态。在运行时能够自动的选择调用哪个方法的现象称为动态绑定。继承层次有一个公共超类派生出来的所有类集合称为继承层次。在继承层次中,某个特定的类到其祖先的路径称为该类的继承链。多态一个类变量既可以引用一个类对象,也可与引用其的任何一个子对象。但是,不能将一个超类的引用赋给子类变量。方法调用重载解析:编译器根据参数找到调用的对应的重载方法的过程。注:允许子类将覆盖方法的返回类型定义为原返回类型的子类型。private方法,static方法、final方法或者构造器,这些“方法”编译器可以准确的知道该调用哪个方法,这种调用方式称为静态绑定。虚拟机会预先为每个类创建一个方法表,其中列出了所有方法的签名和实际调用的方法。这样一来,在真正调用方法的时候虚拟机仅查找这张表。动态绑定的特性:无需对现存代码进行修改,就可以对程序进行扩展。 警告:在覆盖一个方法的时候,子类方法不能低于超类方法的可见性。阻止继承:final类和方法类的特定方法被声明为fina,子类将不能覆盖这个方法。如果将类声明为final,只有其中的方法自动成为final,而不包括域。强制类型转换只能在继承层次内进行转换。在将超类转换成子类前,应该用instanceof进行检查。抽象类类即使不含抽象方法,也可以将类声明为抽象类。抽象类不能被实例化。可以定义一个抽象类的对象变量,但是它只能引用非抽象子类的对象。编译器只允许调用声明在类中的方法。受保护访问java用于控制可见性的4个访问修饰符:1. 仅对本类可见——private。2. 对所有类可见——public。3. 对本包和多有子类可见——protected。4. 对本包可见——默认,不需要修饰符。Object类:所有类的超类equals方法Object类中的equals方法用于检验一个对象是否等于另一个对象。相等测试与继承从不同情况设计equals方法:1. 如果子类能够拥有自己相等的概念,则对称性需求经强制性采用getClass进行检测。2. 如果由超类决定相等的概念,那么就可以使用instanceof进行检测,这样可以在不同子类对象之间进行相等比较。 假设某个超类的id作为相等检测标准,并且这个相等概念适用于所有子类,就可以适用instanceof进行检测,并且应该将类名.equals方法声明为final。编写完美equals方法建议:1. 显示参数名为otherObject。2. 检测this和otherObject是否引用同一个对象。3. 检测otherObject是否为null,如果为null,返回false。4. 比较this与otherObject是否属于同一个类。5. 将otherObject转换为相应的类类型变量。6. 现在进行比较。如果在子类重新定义equals方法,就要在其中调用super.equals(other)。对于数组类型的域,可以使用静态的Arrays.equals方法检测相应的数组元素是否相等。hashCode方法API:java.util.Object、java.util.Objects、java.lang.基本数据类型的包装类、java.util.Arrays;这些类的hashCode相关方法。散列码:是由对象导出的一个整形值。由于hashCode方法定义在Object类中,因此每个对象都有一个默认的散列码,其值为对象的存储地址。如果重新定义equals方法,就必须重新定义hashCode方法,以便用户可以将对象插入到散列表中。最好使用null安全的方法Objects.hashCode。基本数据类型使用包装类名.hashCode来避免创建包装类对象。equals和hashCode的定义必须一致,如果x.equals(y)放回true,那么x.hashCode()就必须与y.hashCode()相同。 如果存在数组类型的域,那么可以使用静态的Arrays.hashCode方法计算一个散列码,这个散列码由元素的散列码组成。toString方法API:java.lang.Object的toString方法和eaqals方法,在自定义的类中,应该覆盖这两个方法。toString只要目的:只要一个对象与一个字符串通过操作符“+”连接起来,java就会自动的调用toString方法,以便获得这个对象的字符串描述。在调用x.toString方法时可以用""+x代替。如果x是基本类型,这条语句照样能执行。Object类定义了toString方法,用来打印输出对象所属的类名和散列码。泛型数组列表API:java.util.ArrayList<E>ArrayList:是一个采用类型参数的泛型类。使用add方法可以将元素添加到数组列表中。数组列表管理着对象引用的一个内部数组。如果调用add且内部数组已经满了,数组列表就会自动创建一个更大的数组,并将所有的对象从较小的数组中拷贝到较大的数组中去。访问数组列表元素API:java.util.ArrayList<T>使用add方法数组添加新元素(可以插入中间元素),而不用set方法,set方法会覆盖已经存在的元素内容。对数组实施插入和删除的效率比较低;如果数组储存元素比较多,就应该使用链表。类型化与原始数组的兼容性一旦能保证不会造成严重后果,可以用@SuppressWarnings("unchecked")标注来标记完成这个变量能够接受类型转换。如下:@SuppressWarnings("unchecked") ArrayList<A> res = (ArrayList<A>) a.find(b);//fields warning.对象包装器与自动装箱API:java.lang.Integer、javaj.lang.NumberFormat所有基本类型都有一个对应的包装(器)类;整形和浮点型的包装器的公共超类Number。对象包装类是不可变的,即一旦构造了包装器,就不允许改变包装在其中的值。对象包装器还是final,因此不能定义它们的子类。警告:由于每个值分别包装在对象中,所以ArrayList<Integer>的效率远低于int[]的效率,因此乐意用它构造小型集合。自动装箱,如:list.add(3);自动转为list.add(Integer.valueOf(3));两个包装类比较时,用equals方法。如果在一个条件表达式中混合使用Integer和Double类型,Integer值就会拆箱,提升为double,再装箱为Double。拆箱和装箱是编译器认可的,而不是虚拟机。编译器在生成类的字节码时,插入必要的方法调用。虚拟机只是执行这些字节码。参数数量可变方法如:public static String method(String a,Object... args){..........}“...”是java代码的一部分,它表明这个方法可以接受任意数量的对象(除a参数外)。可以这样调用此方法:String m = method("cc",1,"bb","xx");编译器经new Object[]{1,"bb","xx"}传递给method方法。枚举类API:java.lang.Enum<E>一个简单的枚举类:public enum Size(){SMALL,MEDIUM,LARGE,EXTRAL_LARGE}在比较两个枚举类型是相等时,永远不要调用equals方法,而直接使用“==”就可以了。如果需要的话,可以在枚举类型中添加一些构造器、方法和域;构造器只是在构造枚举时调用。 所有的枚举类型都是Enum的子类。反射API:java.lang.Class、java.lang.reflect.Constructor、java.lang.reflect.Field、java.lang.reflect.Method、java.lang.reflect.Modifier反射库提供了一个非常丰富且精心设计的工具集,方便编写动态操纵java代码的程序。这项功能被大量用于javaBean中,他是java组件的体系结构。能够分析类能力的程序称为反射。Class类一个Class对象将表示一个特定类的属性获取Class类型对象的方法:1. 实例对象(名).getClass()。2. 类型(名).class。3. Class.forName(类名路径或接口名的字符串)。无论何时,这个方法都要提供异常处理器。Class类实际上是一个泛型类。如Employee.class的类型是Class<Employee>。虚拟机为每个类型管理一个Class对象。因此可以用“==”运算符实现两个类对象比较。Class类型.newInstance()调用默认构造器初始化新创建的对象。如果这个类没有默认构造器,就会抛异常。如果要使用有参构造器,必须使用Constructor类的newInstance方法。捕获异常API:java.lang.Throwable异常有两种:未检查异常和已检查异常。利用Throwable类的printStackTrace方法打印出栈的轨迹。Throwable是Exception的超类。对于检查异常只需提供一个异常处理器。在运行时使用反射分析对象API:java.lang.reflect.ObjectAnalyzerAccessibleObject是Field、Method、Constructor类的公共超类。已知Class类对象,可以使用getDeclareFields获取所有数据域,然后使用Field类的setAccessible方法将所有域设置为可访问,这时可以用Field类的get和set方法获取每个域的值或者设置新值。泛型toString方法查看任意对象内部信息问题:循环引用可能导致无限递归,ObjectAnalyzer将记录已经被访问的对象。例:public String toString(){return new ObjectAnalyzer().toString(实例对象);}使用反射编写泛型编写数组代码API:java.lang.reflect.Array如过一个User[]对象零时转成Object[]数组,然后再把它装换回来是可以的,但从一开始就是Object[]的数组却永远不能转成User[]数组。整形数组类型int[]可以转换成Object,但不能转换成对象数组。使用反射编写泛型数组实现方法:public static Object goodCopyOf(Object a,int newLength){Class cl = a.getClass();//获取数组a的类对象if(!cl.isArray()) return null;//确认a是一个数组Class componentType = cl.getComponentType();//使用Class类的getComponentType方法确定数组的对应类型int length = Array.getLength(a);Object newArray = Array.newInstance(componentType, newLength);System.arraycopy(a, 0, newArray, 0, Math.min(length, newLength));//复制return newArray;}使用反射编写泛型数组实现方法:public static Object goodCopyOf(Object a,int newLength){Class cl = a.getClass();//获取数组a的类对象if(!cl.isArray()) return null;//确认a是一个数组Class componentType = cl.getComponentType();//使用Class类的getComponentType方法确定数组的对应类型int length = Array.getLength(a);Object newArray = Array.newInstance(componentType, newLength);System.arraycopy(a, 0, newArray, 0, Math.min(length, newLength));//复制return newArray;}继承的设计技巧1. 将公共操作和域放在超类。2. 不要使用受保护的域。3. 使用继承实现“is-a”关系。4. 除非所有的方法都有意义,否则不要使用继承。5. 在覆盖方法时,不要改变预期行为。6. 使用多态而非类型信息。7. 不要过多的使用反射。接口、lambda表达式、与内部类接口概念API:java.lang.Comparable<T>、java.util.ArraysArrays类中的sort方法可以对对象数组进行排序,但要满足一下前提:对象所属的类必须实现Comparable接口。接口中所有的方法都属于public,因此可以省略public关键字。接口不能含有实例域。提供实例域和方法实现的任务应该由实现接口的那个类来完成。接口的特性java规范建议不写多余的关键字。一个类可以实现多个接口。静态方法jse8允许在接口中添加静态方法。默认方法可以为接口方法提供一个默认实现(defaule修饰的方法)。默认方法可以调用任何其他方法。解决默认方法冲突1. 超类优先。2. 接口冲突。必须覆盖这个方法,或可以选择两个方法中的一个。注意:千万不要让一个默认方法重新定义Object类中的某个方法。接口示例Comparator 接口(Arrays.sort的第二个版本)一个数组和一个比较器(comparator)作为参数,比较器是实现Comparator接口类的实例。compare方法要在比较器对象上使用,而不是在字符串本身上调用。对象克隆cloneable是java提供的一组标记接口。标记接口不包含任何方法,他的唯一作用是允许在类查询中使用instanceof。即使clone的默认拷贝(浅拷贝)实现能够满足要求,还是需要实现Cloneale接口,将clone方法重新定义为public,再调用super.clone().深拷贝中,主要是对可变变量的拷贝(除开没有更改器的常量、不可变对象外)。前提是这些不可变子对象也实现了clone。深拷贝大致步骤:1. 实现Cloneable接口。2. 实现类中定义clone方法,先调用父类clone,再拷贝可变子对象,返回拷贝对象。3. 外部调用clone方法就实现深拷贝。lambda表达式API:java.util.function 常用函数式接口、基本类型函数式接口lambda表达式是一个可传递的代码块,可以在以后执行一次或多次。lambda表达式语法(参数)-> 表达式、(参数)->{表达式}即使lambda表达式没有参数仍要提供空括号,就像无参方法一样。如果可以推导出一个lambda表达式的参数类型,则可忽略其参数类型。如果方法只有一个参数,而且这个参数的参数类型可以推导的出,可省略小括号。无需指定lambda表达式的返回类型,返回类型总是会由上下文推导得出。如果一个lambda表达式只在某些分支返回一个值,而在另一些分支不返回值,这是不合法的。函数式接口对于只有一个抽象方法的接口,需要这个接口的对象时,就可以提供一个lambda表达式这种接口称为函数式接口。最好把lambda表达式看作是一个函数,而不是对象。另外lambda表达式可以传递到函数式接口。 不能把lambda表达式赋给类型为Object的变量,Object不是一个函数式接口。方法引用有时可能已经有现成的方法可以完成你想要传递到其他代码的某个动作。用::操作符分隔方法与对象类名,主要有以下3种情况:1. object::instanceMethod。如System.out::println等价于System.out.println(x)2. Class::staticMethod。如Math::pow等价于(x,y)->Math.pow(x,y)3. Class::instanceMethod。如String::compareToIngnoreCase等同于(x,y)->x.compareToIngnoreCase(y)可以在方法中引用使用this参数。this::equals等同于x->this.equals(x)。使用super也是合法的,super::instanceMethod。使用this作为目标,会调用给指定方法的超类版本。构造器引用利用new来使用构造器引用。如Person::new。可以用数组类型建立构造器引用。如:int[]::new等价于x->new int[x]。变量作用域lambda表达式有3个部分:1. 代码块。2. 参数。3. 自由变量值。lambda表达式可以捕获外围作用域变量的值。要确保所捕获的值是明确定义的。lambda表达式捕获的变量必须实际上是最终变量(初始化后不再赋值的变量)。lambda表达式中声明一个与局部变量同名的参数或者局部变量是不合法的。处理lambda表达式使用lambda表达式的重点是延迟执行。如以下原因:1. 在一个单独线程运行的代码。2. 多次运行代码。3. 在算法的适当位置运行代码。4. 发生某种情况时运行代码。5. 只在必要事才运行代码。 如果是自己设计的接口,其中只有一个抽象方法,可以用@FunctionalInterface注解来标记这个接口。内部类内部类访问对象状态内部类既可以访问自身的数据域,也可以访问创建他的外部对象的数据域。内部类总有一个隐式引用outer,只想创建他的外部类对象。外部类引用在构造器中设置。如内部类没有定义构造器,会默认生成一个参数含外部引用的构造器。如构造器:public Time(Talk t){outer = t; }注意:outer不是java关键字。内部类的特殊语法规则内部类声明的所有静态域都必须是final。内部类不能有static方法。表达式OuterClass.this表外围类引用。局部内部类局部类不能用public和private访问说明进行声明。他的作用域被限定在声明这个局部类的块中。他可以对外部世界完全的隐藏起来。由外部发方法访问变量局部内部类不仅能够访问包含他们的外部类,还可以访问局部变量,局部变量必须有final修饰。匿名内部类通常语法格式:new SuperType(construction paramters){inner class methods and data}匿名类不能有构造器,而是将构造器参数传给超类构造器。尤其是在内部类实现接口的时候,不能有任何构造参数。如下:new InterfaceType(){method snd data} 习惯用匿名内部类实现事件监听和其他回调。如今最好的做法是使用lambda表达式匿名内部类对于equals的方法使用要当心。new Object(){}.getClass().getEnclosingClass() //get class of static method静态内部类注:1. 在内部类不需要访问外围类对象的时候,应该使用静态内部类。2. 静态类可以有静态域和方法。3. 声明在接口中的内部类自动成为static和public类。
Mar 12, 2019 9:55:18 AM
688