构造方法
构造方法又叫构造器,是类的一种特殊方法,它的主要作用是完成对新对象的初始化。它有几个特点:
-
构造器的修饰符可以是public、缺省、protected、private
-
一个类可以定义多个不同的构造器,即构造器的重载
-
未手动定义构造器,系统会自动给类生成一个默认无参构造器(也叫默认构造器)
可以使用 javap指令,反编译查看
// 默认构造器 class Dog{ Dog(){ } } -
一旦定义手动定义构造器,默认构造器就被覆盖,不能在使用默认的无参构造器,除非显示的定义无参构造器
class Dog{ // 手动定义无参构造器 Dog(){ } public Dog(String dName){ // ... } } -
构造器没有返回值,也不能写 void
-
方法名必须和类名一样
-
参数列表和成员方法的规则一样
-
构造器的调用由系统完成
作用域
全局变量
也就是类的成员属性,作用域为整个类体,可以不赋值直接使用,因为有默认值。
局部变量
除了成员属性之外的其他变量(代码块、方法中),作用域为定义它的代码块中,必须赋值后才能使用,因为没有默认值。
使用细节
- 成员变量和局部变量可以重名,访问时遵循就近原则
- 在同一个作用域中,比如在同一个成员方法中,两个局部变量不能重名
- 成员变量声明周期较长,伴随着对象的创建而创建,伴随着对象的死亡而死亡。局部变量,声明周期较短,伴随着它的代码块的执行而创建,伴随着代码块的结束而死亡。
- 全局变量可以加修饰符,局部变量不可以加修饰符(Modifier 'xxxx' not allowed here)。
包
包的使用细节
访问修饰符
修饰符权限
| NO | 范围 | private | default | protected | public |
|---|---|---|---|---|---|
| 1 | 同一包中的同一类 | 可以访问 | 可以访问 | 可以访问 | 可以访问 |
| 2 | 同一包中不同类 | 不可以访问 | 可以访问 | 可以访问 | 可以访问 |
| 3 | 不同包中的子类 | 不可以访问 | 不可以访问 | 可以访问 | 可以访问 |
| 4 | 不同包中的非子类 | 不可以访问 | 不可以访问 | 不可以访问 | 可以访问 |
注意事项
- 修饰符可以用来修饰类中的属性、方法以及类
- 只有默认的和public才能修饰类,并且遵循以上访问权限特点
- 成员方法的访问规则和属性完全一样
多态
方法的多态
-
重写方法
不同子类调用重写父类的方法,体现多态
-
重载方法
方法名相同,参数列表和返回值不同,体现出来多态
对象的多态
对象多态的前提是两个类存在继承关系。
向上转型
本质:父类的引用指向子类的对象
语法:父类类型 引用名 = new 子类类型();
特点:编译类型看左边,运行类型看右边,可以调用父类中所有成员(需要遵循访问权限规则),不能调用子类中特有成员,最终运行效果看子类具体实现。
// Animal.java
public class Animal{
private String name;
public void cry(){
sout("动物叫。。。");
}
public void info(){
sout("我叫" + this.name);
}
private void sleep(){
sout(this.name + "睡觉");
}
public Animal(String name){
this.name = name;
}
public String getName(){
return this.name;
}
public void setName(String name){
this.name = name;
}
}
// Cat.java
public class Cat extends Animal{
@Override
public void cry(){
sout("小猫喵喵叫。。。");
}
public void run(){
System.out.println(this.getName() + "跑步。");
}
public Cat(String name){
super(name);
}
}
// Dog.java
public class Dog extends Animal{
@Override
public void cry(){
sout("小狗汪汪叫。。。");
}
public Dog(String name){
super(name);
}
}
// Poly01.java
public class Poly01{
public static void main(String[] args){
// animal 的编译类型为Animal,运行类型为 Cat
Animal animal = new Dog("大黄狗");
// animal 的编译类型为Animal,运行类型为 Cat
animal = new Cat("大橘猫");
// 不能调用子类特有的方法
// 因为在编译阶段 animal 的类型为 Animal,而Animal 类型不存在 run()方法,报错
// animal.run();
// 编译时,animal 为 Animal 类型,Animal 类存在 info 方法,
// 运行时,animal 为 Cat 类型,Cat 类不存在 info 方法,调用父类的 info 方法
animal.info();
// 遵循修饰符规则,不能使用父类 private 成员
// animal.sleep(); 报错
}
}
向下转型
语法:子类类型 引用名 = (子类类型) 父类引用;
注意:父类引用必须指向当前类型的实例
// 向下转型
// cat 的编译类型和运行类型都是 Cat
// 父类的引用必须指向的是当前目标类型的对象,即 animal 指向的是 Cat 类的实例(见Poly02.java)
Cat cat = (Cat) animal;
// Dog dog = (Dog) animal; // 报错,因为此时 animal 引用的是 Cat 类型
// 可以调用子类特有的方法
cat.run();
JAVA 动态绑定机制
在java 面向对象三大特性——继承篇中,我们说过java 中查找方法的顺序为 : 本类方法—>父类方法—>更高一级的父类—>......Object(顶层父类) 。然而,在某些情况下,这样的原则也会被凌驾。今天我们要说的java动态绑定机制,就是这样一个区别于继承机制的例外。
- 当通过对象的形式调用方法时,该方法会和堆内存中真正的该对象——的内存地址绑定,且这种绑定关系会贯穿方法的执行全过程。这就是所谓的“动态绑定机制”。
- 当通过对象的形式调用属性时,不存在动态绑定机制,符合继承机制——即java中查找变量的顺序 : 局部变量—>成员变量—>父类—>更高的父类—>......—>Object 。
-
当 B 类的 sum 和 sum1 方法不被注释
40
30
-
当 B 类的 sum 和 sum1 方法被注释
30
20
I/O流
文件
文件流
文件在程序中是以流的形式来操作的
流:数据在数据源(文件)和程序(内存)之间经历的路径
输出流:数据从程序(内存)到数据源(文件)之间的路径
输入流:数据从数据源(文件)之间到程序(内存)之间的路径
创建文件
案例:在E盘下,创建文件 news1.txt、news2.txt、news3.txt,用三种不同的方式创建。
File newFile = new File(String filePath);
File newFile2 = new File(String parentDir, String childPath);
file newFile3 = new File(File ParentFile, String childPath);
文件信息
- getAbsolutePath(); 文件绝对路径
- getName(); 文件名字
- getParent(); 文件父级目录
- length(); 文件大小(字节)
- exists(); 文件是否存在
- isFile(); 是不是一个文件
- isDirectory(); 是不是一个目录
I/O流的分类
-
按操作数据单位不同分
- 字节流
- 字符流
字节和字符的区别?
-
按数据流向不同
- 输入流
- 输出流
-
按流的角色不同
- 节点流
- 处理流/包装流
| 抽象基类 | 字节流 | 字符流 |
|---|---|---|
| 输入流 | InputStream | Reader |
| 输出流 | OutputStream | Writer |
InputStream 字节输入流
InputStream 是抽象类 是所有类字节输入流的超类
InputStream 常用子类
FileInputStream (Java SE 11 & JDK 11 ) (runoob.com)
- FileInputStream 文件输入流
- ObjectInputStream 对象字节输入流
- BufferedInputStream 缓冲字节输入流
OutputStream 字节输出流
OutputStream 是抽象类 是所有类字节输出流的超类
OutputStream 常用子类
FileOutputStream (Java SE 11 & JDK 11 ) (runoob.com)
泛型
泛型的理解和好处
不使用泛型
import java.util.ArrayList;
class Dog{
private String name;
private int age;
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
public class Generic01 {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
// ArrrayList 会把添加进去的对象进行向上转型,即 Dog -> Object
arrayList.add(new Dog("大黄",3));
arrayList.add(new Dog("二黄",2));
arrayList.add(new Dog("三黄",1));
// arrayList.add(new Cat("大橘", 5)); 不小心添加一只猫
for(Object o : arrayList){
Dog dog = (Dog)o; // 向下转型
System.out.println(dog.getName() + dog.getAge());;
}
}
}
不使用泛型的问题:
- 不能对加入到集合 ArrayList 中的数据类型进行约束(不安全)
- 遍历的时候,需要进行类型转换,如果集合中数据量较大,对效率有影响
使用泛型
public class Generic02 {
public static void main(String[] args) {
ArrayList<Dog> arrayList = new ArrayList<Dog>();
arrayList.add(new Dog("大黄",3));
arrayList.add(new Dog("二黄",2));
arrayList.add(new Dog("三黄",1));
// arrayList.add(new Cat("大橘", 5)); 此时泛型会编译报错
for(Dog dog : arrayList){
// Dog dog = (Dog)o; // 不需要向下类型转换
System.out.println(dog.getName() + dog.getAge());
}
}
}
泛型的好处:
- 编译时检查添加元素的类型,提高了安全性
- 减少类型转换的次数,提高效率
泛型注意事项
-
类型参数只能是引用类型,不能是基本数据类型
ArrayList<String> arrayList = new ArrayList<>(); ArrayList<int> arrayList = new ArrayList<>(); // Type argument cannot be of primitive type -
在给泛型指定具体类型后,可以传入该类型或其子类,本质上就是向上转型
package com.hsy.generic; public class Generic03 { public static void main(String[] args) { Pig<A> pig = new Pig<>(new A()); // 编译时和运行时类型都是 A pig.getClassName(); // class com.hsy.generic.A // 在给泛型指定具体类型后,可以传入该类型或其子类,本质上就是向上转型 Pig<A> pig2 = new Pig<>(new B()); // 编译时类型是 A,运行时类型是 B pig2.getClassName(); // class com.hsy.generic.B } } class A{} class B extends A{} class Pig<E>{ E e; public Pig(E e) { this.e = e; } public void getClassName(){ System.out.println(e.getClass()); } } -
泛型默认类型为 Object
ArrayList arrayList = new ArrayList(); // 等价于 ArrayList<Object> arrayList = new ArrayList<>();
泛型声明
泛型类
class ClassName<T, K, R, ...>{
}
注意事项:
- 普通成员可以使用泛型(属性、方法)
泛型接口
interface InterfaceName<T,K>{}
泛型实例化
面向对象
对象在内存中的存储
说明
- 执行 Cat cat = new Cat(); 创建 cat 对象时,会在方法区中加载 Cat 类属性和方法信息,且只会加载一次
- 在堆中分配空间,进行默认初始化
- 把地址赋给p,p就指向堆中的对象
- 字符串存储在方法区的常量池中
枚举类
异常
异常体系图
- 异常分为两大类:运行时异常和编译时异常
- 运行时异常编译器检查不出来。一般指编程时的逻辑错误面试程序员应该避免其抛出异常。java.lang.RuntimeException类及其子类都是运行时异常。对于运行时异常可以不做处理,因为这类异常很普遍,若全处理可能会对程序的可读性和运行效率产生影响。
- 编译时异常,是编译器要求必须处理的异常。
常见运行时异常
-
NullPointerException 空指针异常
// 当使用一个引用指向 null 的对象的成员时 String name = null; sout(name.length()); -
ArithmeticException 数学运算异常
-
ArrayIndexOutOfBoundsException 数组下标越界异常
-
ClassCastException 类型转换异常
class A {} class B extends A {} class C extends A {} public class ClassCastExceptionTest { public static void main(String[] args) { A a = new B(); B b = (B)a; C c = (C)a; // ClassCastException 类型转换异常 } } -
NumberFormatException 数字格式不正确异常
// 当把字符串转换为数值类型,但该字符串不能转换为适当格式时,抛出该异常 String name = "衡盛永"; int num = Integer.parseInt(name);编译异常
异常处理机制
try-catch-finally
try {
String name = null;
System.out.println(name.length()); // 异常
System.out.println("异常发生后try中的的代码不再被执行,直接进入到catch块");
} catch (Exception e) {
System.out.println(e.getMessage());; // 输出异常信息
}
System.out.println("程序继续执行");
可以有多个catch语句捕获不同的异常,要求父类异常在后,子类异常在前,比如(Exception 在后,NullPointerException 在前),如果发生异常,只会匹配一个catch。
try {
String name = "衡盛永";
name = null;
System.out.println(name.length()); // NullPointerException
int num1 = 10;
int num2 = 0;
int res = num1 / num2;
}
catch(NullPointerException e){
System.out.println(e.getMessage()); // 捕获并处理异常,后面的catch不再执行
}
catch(ArithmeticException e){
System.out.println(e.getMessage());
}
catch (Exception e) {
e.printStackTrace();
}
try{
// 可能会发生异常的代码
} finally {
// 如果发生异常,执行完 finally 块的代码,程序直接直接退出
}
// 如果发生异常,后面的代码不再被执行
练习题
如果用户输入的不是一个整数,就提示他反复输入,直到输入一个整数为止。
throws
注意
1. try-catch-finally 和 throws 方式二选一
2. 如果既没有显式的try-catch-finally也没有 throws,那么默认是使用throws的,这就是当没有对异常进行处理时JVM最终会输出异常信息并退出程序的原因。
数据库
分组统计
测试表
CREATE TABLE dept( -- 部门表
deptno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0, -- 部门编号
dname VARCHAR(20) NOT NULL DEFAULT "", -- 部门名称
loc VARCHAR(13) NOT NULL DEFAULT "" -- 部门地址
);
INSERT INTO dept VALUES(10, 'ACCOUNTING', 'NEW YORK'), (20, 'RESEARCH', 'DALLAS'), (30, 'SALES', 'CHICAGO'), (40, 'OPERATIONS', 'BOSTON');
反射
反射快速入门
package com.hsy.reflect;
import java.io.FileInputStream;
import java.lang.reflect.Method;
import java.util.Properties;
@SuppressWarnings("all")
public class Reflect01 {
public static void main(String[] args) throws Exception {
// 根据 re.properties 配置文件,创建 Cat 对象并调用 hi 方法
// 反射
// 使用 Properties 类,可以读写配置文件
Properties properties = new Properties();
properties.load(new FileInputStream("src\re.properties"));
String classfullPath = properties.get("classfullPath").toString();
String methodName = properties.get("method").toString();
System.out.println(classfullPath instanceof String);
System.out.println(methodName instanceof String);
// 反射机制
// 加载类,返回 Class 类型的对象 cls
Class cls = Class.forName(classfullPath);
// 通过 cls 得到加载的类 com.hsy.reflect.Cat 类的实例
Object o = cls.newInstance();
System.out.println(o.getClass()); // 获取对象的运行时类型 Cat
// 通过 cls 得到加载类 com.hsy.reflect.Cat 的成员方法 methodName'hi' 对象
Method method1 = cls.getMethod(methodName);
// 通过成员方法对象实现方法的调用
method1.invoke(o); // 传统方法 对象.方法名() 反射机制 方法对象.invoke(对象);
}
}
class Cat{
private String name = "招财猫";
public void hi(){
System.out.println("hi" + this.name);
}
}
反射原理
- 反射机制允许程序在执行期间借助于Reflection API 取得任何类的内部信息(比如成员变量、构造器、成员方法等等),并能操作对象属性及方法。反射在设计模式及框架底层都会用到。
- 加载完类后,在堆中就生成了一个Class类型的对象(一个类只有一个Class对象,即一个类只能被加载一次),这个对象包含了类的完整结构信息。通过这个对象得到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,形象的称之为反射。
数据库
创建数据库
数据库函数
分组函数
统计函数
字符串函数
数学函数
日期函数
-- 在MYSQL中,日期类型可以直接比较,需要注意格式:'YYYY-MM-DD'
-- 查询1992.1.1后入职的员工
SELECT * FROM emp
WHERE hiredate > '1992-01-01';
加密函数
流程控制函数
IF(expre1, expre2, expre3) -- 如果expre1为TRUE,则返回expre2,否则返回expre3
-- 如果comm是NULL,则显示0.0,判断是否为NULL,要使用 IS NULL,判断不为 NULL 使用 IS NOT NULL
SELECT IF(comm IS NULL, 0.0, comm) FROM function_if_employee;
IFNULL(expre1, expre2) -- 如果expre1不为NULL,则返回expre1,否则返回expre2
SELECT IFNULL(comm, 0.0) FROM function_if_employee;
-- 如果job是 CLERK,则显示 职员,如果是 SALEMAN,则显示 销售人员,如果是 MANAGER,则显示 经理,其他正常显示
SELECT ename,
(SELECT CASE
WHEN job = 'CLERK' THEN '职员'
WHEN job = 'SALEMEN' THEN '销售人员'
WHEN job = 'MANGER' THEN '经理'
ELSE job END) AS job, job
FROM function_if_employee;