目录
调试技能
- 双击行号左侧区域:设置断点
- F11:启动调试程序
- F5: 跳转到方法内部
- F6: 单步调试
- F8: 跳转到下一处断点
一维数组
基本语法:
int[] arr = new int[10];
//冒泡排序
package basic;
public class SortDemo {
public static void main(String[] args) {
int[] unsortArr = {34,53,12,32,56,17};
System.out.println("排序前的数组元素为:");
for(int num:unsortArr) {
System.out.println(num);
}
//冒泡排序,定了n-1个位置,最后一个位置也是确定的
for(int index = 0; index < unsortArr.length - 1; index++) {
// 如果已经确定了 i 个元素,自然就不需要再和他们进行比较了
for(int comp = 0; comp < unsortArr.length - index - 1; comp++) {
if (unsortArr[comp + 1] < unsortArr[comp]) {
int temp = -1;
temp = unsortArr[comp + 1];
unsortArr[comp + 1] = unsortArr[comp];
unsortArr[comp] = temp;
}
}
}
System.out.println("排序后的数组元素为:");
for(int num:unsortArr) {
System.out.println(num);
}
}
}
二维数组
基本语法
创建一个三行三列的int类型的数组
int[][] arr = new int[3][3];
创建一个三行不定列数的float数组
float[][] arr = new float[3][];
arr[0] = new float[4];
arr[1] = new float[3];
arr[2] = new float[5];
多态
编译时多态
设计时多态,一般表现形式为方法的重载
运行时多态
程序运行时动态决定调用哪个方法,必须满足继承关系,而且是父类引用指向子类对象
向上向下转型
package basic;
import natapp.liujinliang.Father;
import natapp.liujinliang.Mother;
import natapp.liujinliang.Parents;
public class Poly {
public static void main(String[] args) {
System.out.println("向上转型==========");
Parents m = new Mother();
Parents f = new Father();
System.out.println("向下转型==========");
Mother mom = (Mother) m;
Father daddy = (Father) f;
//向上转型:可以调用子类重写父类的方法以及父类派生的方法,无法调用子类特有的方法
m.test();
f.test();
//抽象类的作用:不可以被实例化,这就避免了无用类的实现
//抽象方法作用:提醒开发者需要实现父类的哪些方法
mom.love();
daddy.love();
//向下转型:必须使用强制类型转换,可以调用子类特有的方法
mom.shopping();
daddy.drinking();
}
}
package natapp.liujinliang;
public abstract class Parents {
public abstract void love();
public void test(){ }
}
package natapp.liujinliang;
public class Father extends Parents {
@Override
public void love() {
System.out.println("父爱如山");
}
public void drinking() {
System.out.println("男人爱喝酒");
}
public void test() {
System.out.println("爸爸的测试类");
}
}
package natapp.liujinliang;
public class Mother extends Parents {
@Override
public void love() {
System.out.println("母爱如水");
}
public void shopping() {
System.out.println("女人爱购物");
}
public void test() {
System.out.println("妈妈的测试类");
}
}
输入输出流(包括字符流和字节流)
流:一连串流动的字符,以先进先出的方式发送信息的通道。
输出流:程序向目标写数据。
输入流:程序从数据源读数据。
分隔符:Windows是”\”,Linux是”/”
字节流:处理的基本单位单位是单个字节,他通常用来处理二进制数据,比如图形图像。InputStream和OutputStream是其祖先
字符流:处理的基本单位是Unicode字符(大小2字节),通常用来处理文本数据。Reader和Writer是其祖先
package file;
import java.io.File;
import java.io.IOException;
public class FileDemo {
public static void main(String[] args) throws IOException {
// 演示实例1
File file = new File("c:\\liujinliang");
File chFile = new File(file, "io\\test\\score.txt");
// 演示实例2
File file2 = new File("c:\\liujinliang\\io\\test", "score.txt");
// 创建File类的对象
File file1 = new File("d:\\score.txt");
// 判断是文件还是目录
System.out.println(file1.isFile());
System.out.println(file1.isDirectory());
// 创建目录
if(!file.exists()) {
file.mkdir();
}
// 创建文件
if(!file1.exists()) {
file1.createNewFile();
}
}
}
线程
线程生命周期
需要注意的是,唤醒线程的方法notify() || notifyAll()是针对wait()方法的。
进程和线程的概念
进程是指可执行程序并存放在计算机存储器的一个指令序列,他是一个动态执行的过程。线程相当于子程序。
扫描二维码关注公众号,回复: 2658278 查看本文章
进程 | 线程的创建
1、创建一个Thread类,或者一个Thread子类的对象。
2、创建一个实现Runnable接口的类的对象。
Thread类
构造方法 | 说明 |
---|---|
Thread() | 创建一个线程对象 |
Thread(String name) | 创建一个具有指定名称的线程对象 |
Thread(Runnable target) | 创建一个基于Runnable接口实现类的线程对象 |
Thread(Runnable target, String name) | 创建一个基于Runnable接口实现类并且具有指定名称的线程对象 |
方法 | 说明 |
---|---|
public void run() | 线程体方法,一般需要重写。 |
public void start() | 启动线程的方法 |
public void sleep(long m) | 线程休眠m毫秒的方法 |
public void join() | 线程抢占资源的方法 |
Runnable接口
只有一个 run() 方法
Runnable是Java中用以实现线程的接口
任何实现线程功能的类都必须实现该接口
反射
基本概念
Java在需要使用到某个类时才会将.class文档载入,在JVM产生java.lang.Class实例代表该文档,.class文档反映了类基本信息,因而从 Class 取得类信息的方式就称为反射(Reflection)。
基本关系梳理:.class文档是在编译过后生成的;Class实例在运行时由JVM生成。
核心:运行时(因为Class实例在运行时创建)
区别:编译时(new关键词创建对象,因为要获取构造函数的信息,所以只能从.class文档查看,而.class文档是在编译时生成)
一言以蔽之:反射从Class获取信息,属运行时;new从.class获取信息,属编译时。然而Class是由.class生成的。
意义:降低耦合度,有很多时候,我们只有在运行时才知道调用那个函数。
使用Class.forName()
背景: 在某些应用中,无法事先知道开发人员要使用哪个类。例如,实现不知道开发人员会是用哪个厂商的的JDBC驱动程序,也就不知道厂商java.sql.Driver接口的类名称为什么,因而必须让开发人员可以事后指定类名称来动态加载类。
只展示核心代码块
public static void main(String[] args) {
try {
Class clz = Class.forName(args[0]);
out.println("类名称:" + clz.getName());
}
}
forName的另一个重载函数是可以指定是否在加载.class文档时是否就执行类内部的静态块。
Class clz = Class.forName("liujinliang.package.jdbc", false, jdbc.getClassLoader();
从Class获取信息
package refDemo;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class ClassViewer {
public static void main(String[] args) {
try {
ClassViewer.view("java.lang.String");
} catch (ArrayIndexOutOfBoundsException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void view(String clzName) throws ClassNotFoundException {
Class clz = Class.forName(clzName);
showPackageInfo(clz);
showClassInfo(clz);
System.out.println("{");
showFieldsInfo(clz);
showConstructorsInfo(clz);
showMethodsInfo(clz);
System.out.println("}");
}
/**
* 包对应的类型是java.lang.Package
* @param clz
*/
public static void showPackageInfo(Class clz) {
Package p = clz.getPackage(); //取得包代表对象
System.out.println("package:" + p.getName());
}
public static void showClassInfo(Class clz) {
int modifier = clz.getModifiers(); //取得类型修饰常数
System.out.println(clz.getName() + "|" + Modifier.toString(modifier) + "|" + (Modifier.isInterface(modifier) ? "interface" : "class"));
}
public static void showFieldsInfo(Class clz) {
// 取得声明的数据成员的代表对象
Field[] fields = clz.getDeclaredFields();
for (Field field : fields) {
// 显示权限修饰,例如public、protected、private
System.out.println(Modifier.toString(field.getModifiers()) + " " + field.getType().getName() + " " + field.getName());
}
}
public static void showConstructorsInfo(Class clz) {
// 取得声明的构造方法代表对象
Constructor[] constructors = clz.getConstructors();
for (Constructor constructor : constructors ) {
System.out.println(Modifier.toString(constructor.getModifiers()) + " " + constructor.getName());
}
}
public static void showMethodsInfo(Class clz) {
Method[] methods = clz.getMethods();
for (Method method : methods) {
System.out.println(Modifier.toString(method.getModifiers()) + " " + method.getReturnType().getName() + " " + method.getName());
}
}
}
从Class建立对象
package student;
public class Student {
private String name;
private Integer score;
public Student() {
}
public Student(String name, Integer score) {
this.name = name;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getScore() {
return score;
}
public void setScore(Integer score) {
this.score = score;
}
@Override
public String toString() {
return name + ":" + score;
}
}
核心代码块
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class TestStudent {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Class clz = Class.forName("student.Student");
Constructor constructor = clz.getConstructor(String.class, Integer.class);
Object obj = constructor.newInstance("Liujinliang", 22);
System.out.println(obj.toString());
// 操作对象方法与成员
Method setter = clz.getMethod("setName", String.class);
setter.invoke(obj, "LiuChao");
Method getter = clz.getMethod("getName");
System.out.println(getter.invoke(obj));
}
}
代理
背景
如果需要在执行某个方法前后需要进行日志记录,你可能会这样撰写:
class HelloSpeaker {
public void hello(String name) {
// 方法开始前留下日志
Logger.getLogger(HelloSpeaker.class.getName()).log(Level.INFO, "hello()方法开始");;
// 程序主要功能
System.out.println("Hello:" + name);
// 方法执行完毕前留下日志
Logger.getLogger(HelloSpeaker.class.getName()).log(Level.INFO,"hello()方法结束");
}
}
如果希望为许多方法都添加日志,或者有一天觉得这些日志比较多余,是不是会觉得添加和删除的代码两会比较烦啊。这就引入了下面将要说的静态代理和动态代理。动态代理比静态代理的优势就是:可以让你不必为特定接口操作特定代理对象,使用动态代理,可使用一个处理者代理多个接口的操作对象。
静态代理
还记不记得之前说过的向上转型啊,这就是一个典型的应用,当然了,从多态的角度来看,就叫运行时多态,真正调用的方法是子类重写父类的方法,在代理类中,真正调用的是别代理对象的相关方法,静态代理的主要缺点就是,一个接口对应一个代理类(特定接口对应特定代理类)。
package proxy;
public interface Hello {
void hello(String name);
}
package proxy;
public class HelloSpeaker implements Hello {
@Override
public void hello(String name) {
System.out.println("Hello:" + name);
System.out.println("I'm Liujinliang");
}
}
package proxy;
import java.util.logging.Level;
import java.util.logging.Logger;
public class HelloProxy implements Hello {
private Hello helloObj;
public HelloProxy(Hello helloObj) {
this.helloObj = helloObj;
}
@Override
public void hello(String name) {
// 方法开始前留下日志
log("hello()方法开始");
// 程序主要功能
helloObj.hello(name);
// 方法执行完毕前留下日志
log("hello()方法结束");
}
private void log(String msg) {
Logger.getLogger(HelloSpeaker.class.getName()).log(Level.INFO, msg);
}
public static void main(String[] args) {
Hello helloProxy = new HelloProxy(new HelloSpeaker());
helloProxy.hello("Liujinliang");
}
}
动态代理
为了解决一个接口一个代理类的局限性,我们想到了动态代理。
package proxy;
public class HelloSpeaker implements Hello {
@Override
public void hello(String name) {
System.out.println("Hello:" + name);
System.out.println("I'm Liujinliang");
}
}
package proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.logging.Level;
import java.util.logging.Logger;
public class LoggingHandler implements InvocationHandler {
private Object target;
public Object bind(Object target) {
this.target = target;
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object res = null;
Logger.getLogger(LoggingHandler.class.getName()).log(Level.INFO, method.getName() + "呼叫开始。。。");
res = method.invoke(target, args);
Logger.getLogger(LoggingHandler.class.getName()).log(Level.INFO, method.getName() + "呼叫结束。。。");
return res;
}
public static void main(String[] args) {
LoggingHandler loggingHandler = new LoggingHandler();
Hello proxy = (Hello) loggingHandler.bind(new HelloSpeaker());
proxy.hello("Liujinliang");
}
}