static关键字的用法:
成员变量可以分为两类:
public class User {
// 静态成员变量
public static String onlineNumber= 161;
}
类名.静态成员变量。(推荐)
对象.静态成员变量。(不推荐)
public class User {
// 实例成员变量
private String name;
private int age;
}
对象.实例成员变量。
注意:两种成员变量各自在什么情况下定义:
静态成员变量:表示在线人数等需要被共享的信息。
实例成员对象:属于每个对象,且每个对象信息不同时(name,age,…等)
public class User {
//静态成员变量
public static int onLineNumber = 161;
//实例成员变量
private String name;
private int age;
public static void main(String[] args) {
//1、类名.静态成员变量
User.onLineNumber++;
//注意:同一个类中访问静态成员变量,类名可以省略不写
System.out.println(onLineNumber);
//2、对象.实例成员变量
User u1 = new User();
u1.age = 20;
u1.name = "孙悟空";
//对象.静态成员变量(不推荐)
u1.onLineNumber++;
User u2 = new User();
u2.name = "猪八戒";
u2.age = 29;
System.out.println(u2.name);
System.out.println(u2.age);
// 对象名称.静态成员变量( 不推荐 )
u2.onLineNumber++;
System.out.println(User.onLineNumber); // 164
}
}
成员方法的分类:
使用场景:
表示对象自己的行为的,且方法中需要访问实例成员的,则该方法必须申明成实例方法。
如果该方法是以执行一个共用功能为目的,则可以申明成静态方法。
public class method {
private String name;
private int age;
//实例方法,无static修饰,属于对象,通常表示对象自己的行为,可以访问对象的成员变量
public void study(){
System.out.println(name +"好好学习,天天向上");
}
//静态方法,有static修饰,属于类,可以被类和对象共享访问
public static void getMax(int a, int b){
System.out.println(a > b ? a : b);
}
public static void main(String[] args) {
//1、类名,静态方法
method.getMax(10,100);
//注意:同一个类中,访问静态成员,可以省略类名不写
getMax(100,200);
//2、对象.实例方法
method m = new method();
m.name = "OK";
m.study();
//3、对象.静态方法(不推荐)
m.getMax(100,3000);
}
}
public class Student {
private String name;
// 1. 实例方法: 无static修饰的,属于对象的
public void study(){
System.out.println(name + "在好好学习~~~");
}
// 2. 静态方法:有static修饰,属于类和对象共享的
public static int getMax(int a , int b){
return a > b ? a : b;
}
public static void main(String[] args) {
// 1. 类名.静态成员方法
System.out.println(Student.getMax(10 , 2));
// 注意:同一个类中访问静态成员类名可以不写
System.out.println(getMax(2 , 10));
// 2. 对象.实例成员方法
// study(); // 会报错
Student s = new Student();
s.name = "猪八戒";
s.study();
// 3. 对象.静态成员方法。(不推荐)
System.out.println(s.getMax(20 , 10));
}
}
工具类: 工具类中定义的都是一些静态方法,每个方法都是以完成一个共用的功能为目的。
工具类的好处: 一是调用方便,二是提高了代码复用(一次编写,处处可用)
为什么工具类中的方法不用实例方法做?
- 实例方法需要创建对象调用,此时用对象只是为了调用方法,这样只会浪费内存。
工具类的定义注意:
public class VerifyTool {
//私有构造器
private VerifyTool(){
}
//静态方法
public static String createCode(int n){
// 1、使用String开发一个验证码
String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
// 2、定义一个变量用于存储5位随机的字符作为验证码
String code = "";
// 3、循环
Random r = new Random();
for (int i = 0; i < n; i++) {
int index = r.nextInt(chars.length());
// 4、对应索引提取字符
code += chars.charAt(index);
}
return code;
}
}
public class Register {
public static void main(String[] args) {
// 验证码:
System.out.println("验证码:" + VerifyTool.createCode(5));
}
}
案例:定义数组工具类
需求:在实际开发中,经常会遇到一些数组使用的工具类。请按照如下要求编写一个数组的工具类:ArraysUtils
public class ArrayUtils {
//把它的构造器私有化
private ArrayUtils(){
}
//静态方法,工具方法
public static String toString(int[] arr){
if(arr != null ){
String result = "[";
for (int i = 0; i < arr.length; i++) {
result += (i == arr.length - 1 ? arr[i] : arr[i] + ", ");
}
result += "]";
return result;
}else {
return null;
}
}
//静态方法,工具方法
public static double getAverage(int[] arr){
// 总和 最大值 最小值
int max = arr[0];
int min = arr[0];
int sum = 0;
for (int i = 0; i < arr.length; i++) {
if(arr[i] > max){
max = arr[i];
}
if(arr[i] < min){
min = arr[i];
}
sum += arr[i];
}
return (sum - max - min)*1.0 / (arr.length - 2);
}
}
public class Test2 {
public static void main(String[] args) {
int[] arr = {10, 20, 30};
System.out.println(arr);
System.out.println(ArrayUtils.toString(arr));
System.out.println(ArrayUtils.getAverage(arr));
int[] arr1 = null;
System.out.println(ArrayUtils.toString(arr1));
int[] arr2 = {};
System.out.println(ArrayUtils.toString(arr2));
}
static访问注意实现:
public class Test {
// 静态成员变量
public static int onLineNumber;
// 实例成员变量
private String name;
public static void getMax(){
// 1、静态方法可以直接访问静态成员,不能访问实例成员。
System.out.println(Test.onLineNumber);
System.out.println(onLineNumber);
inAddr();
// System.out.println(name);
// 3、静态方法中不能出现this关键字
// System.out.println(this);
}
public void run(){
// 2、实例方法可以直接访问静态成员,也可以访问实例成员
System.out.println(Test.onLineNumber);
System.out.println(onLineNumber);
Test.getMax();
getMax();
System.out.println(name);
sing();
System.out.println(this);
}
public void sing(){
System.out.println(this);
}
// 静态成员方法
public static void inAddr(){
System.out.println("我们在黑马程序员~~");
}
public static void main(String[] args) {
}
}
代码块概述:
代码块分为:
public class TestDemo1 {
public static String schoolName;
public static void main(String[] args) {
// 目标:学习静态代码块的特点、基本作用
System.out.println("=========main方法被执行输出===========");
System.out.println(schoolName);
}
/**
特点:与类一起加载,自动触发一次,优先执行
作用:可以在程序加载时进行静态数据的初始化操作(准备内容)
*/
static{
System.out.println("==静态代码块被触发执行==");
schoolName = "菜园";
}
}
==静态代码块被触发执行==
=========main方法被执行输出===========
菜园
public class TestDemo2 {
private String name;
/**
属于对象的,与对象一起加载,自动触发执行。
*/
{
System.out.println("==构造代码块被触发执行一次==");
name = "张麻子";
}
public TestDemo2(){
System.out.println("==构造器被触发执行==");
}
public static void main(String[] args) {
// 目标:学习构造代码块的特点、基本作用
TestDemo2 t = new TestDemo2();
System.out.println(t.name);
TestDemo2 t1 = new TestDemo2();
System.out.println(t1.name);
}
}
==构造代码块被触发执行一次==
==构造器被触发执行==
张麻子
==构造代码块被触发执行一次==
==构造器被触发执行==
张麻子
需求:
在启动游戏房间的时候,应该提前准备好54张牌,后续才可以直接使用这些牌数据。
分析:
public class StaticCodeTest3 {
/**
模拟初始化牌操作
点数: "3","4","5","6","7","8","9","10","J","Q","K","A","2"
花色: "♠", "♥", "♣", "♦"
1、准备一个容器,存储54张牌对象,这个容器建议使用静态的集合。静态的集合只加载一次。
*/
public static ArrayList<String> cards = new ArrayList<>();
/**
2、在游戏启动之前需要准备好54张牌放进去,使用静态代码块进行初始化
*/
static{
// 3、加载54张牌进去。
// 4、准备4种花色:类型确定,个数确定了
String[] colors = {"♠", "♥", "♣", "♦"};
// 5、定义点数
String[] sizes = {"3","4","5","6","7","8","9","10","J","Q","K","A","2"};
// 6、先遍历点数、再组合花色
for (int i = 0; i < sizes.length; i++) {
// sizes[i]
for (int j = 0; j < colors.length; j++) {
cards.add(sizes[i] + colors[j]);
}
}
// 7、添加大小王
cards.add("小");
cards.add("大");
}
public static void main(String[] args) {
System.out.println("新牌:" + cards);
}
}
什么是设计模式(Design pattern):
单例模式:
单例的实现方式很多
- 饿汉单例模式。
- 懒汉单例模式。
- …
饿汉单例设计模式: 在用类获取对象的时候,对象已经提前为你创建好了。
设计步骤:
/** a、定义一个单例类 */
public class SingleInstance {
/** c.定义一个静态变量存储一个对象即可 :属于类,与类一起加载一次 */
public static SingleInstance instance = new SingleInstance ();
/** b.单例必须私有构造器*/
private SingleInstance (){
System.out.println("创建了一个对象");
}
}
//SingleInstance s1 = SingleInstance.instance;
懒汉单例设计模式: 在真正需要该对象的时候,才去创建一个对象(延迟加载对象)。
设计步骤:
public class SingleInstance2 {
/**
2、定义一个静态的成员变量用于存储一个对象,一开始不要初始化对象,因为人家是懒汉
*/
private static SingleInstance2 instance;
/**
1、私有构造器啊
*/
private SingleInstance2(){
}
/**
3、提供一个方法暴露,真正调用这个方法的时候才创建一个单例对象
*/
public static SingleInstance2 getInstance(){
if(instance == null){
// 第一次来拿对象,为他做一个对象
instance = new SingleInstance2();
}
return instance;
}
//SingleInstance2 s1 = SingleInstance2.getInstance();
什么是继承?
使用继承的好处:
案例:
解决方案:把相同的属性和行为抽离出来,可以降低重复代码的书写,抽取出来放到何处呢?
继承设计规范:
为什么?
public class Role {
private String name;
private int age;
public void queryCourse(){
System.out.println(name + "开始查看课程信息");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class Student extends Role{
private String classname;
public void writeInfo(){
System.out.println(getName()+"说:今天学习很不错");
}
public String getClassname() {
return classname;
}
public void setClassname(String classname) {
this.classname = classname;
}
}
public class Test {
public static void main(String[] args) {
Student s = new Student();
s.setName("张松松");//父类的
s.setAge(25);//父类的
s.setClassname("菜园子第一期");//子类的
System.out.println(s.getName());
System.out.println(s.getAge());
System.out.println(s.getClassname());
s.queryCourse(); // 父类的
s.writeInfo(); // 子类的
}
}
注意:父类空间与子类空间链接在一起,共用一个地址。
继承的特点:
1、子类是否可以继承父类的构造器?
2、子类是否可以继承父类的私有成员?
3、子类是否可以继承父类的静态成员?
Java支持多层继承
Object特点:
在子类方法中访问成员(成员变量、成员方法)满足:就近原则
如果子父类中,出现了重名的成员,会优先使用子类的,此时如果一定要在子类中使用父类的怎么办?
变量
public class ExtendsDemo {
public static void main(String[] args) {
Wolf w = new Wolf();
System.out.println(w.name); // 子类的
w.showName();
}
}
class Animal{
public String name = "父类动物";
}
class Wolf extends Animal{
public String name = "子类动物";
public void showName(){
String name = "局部名称";
System.out.println(name); // 局部的
System.out.println(this.name); // 子类name
System.out.println(super.name); // 父类name
}
}
方法
public class ExtendsDemo2 {
public static void main(String[] args) {
Student s = new Student();
s.run(); // 子类的
System.out.println("-----------");
s.go();
}
}
class People{
public void run(){
System.out.println("可以跑~~");
}
}
class Student extends People{
public void run(){
System.out.println("学生跑的贼快~~");
}
public void go(){
run(); // 子类的
super.run(); // 父类的
}
}
什么是方法重写?
方法重写的应用场景
案例演示:
@Override重写注解
public class Phone {
public void call(){
System.out.println("打电话开始~~~");
}
public void sendMessage(){
System.out.println("发送短信开始~~~");
}
}
public class NewPhone extends Phone{
@Override
public void call(){
super.call();
System.out.println("支持视频通话~~~");
}
@Override
public void sendMessage(){
super.sendMessage();
System.out.println("支持发送图片和视频~~~");
}
}
方法重写注意事项和要求
子类继承父类后构造器的特点:
为什么?
怎么调用父类构造器的?
public class Animal {
public Animal(){
System.out.println("==父类Animal无参数构造器被执行===");
}
}
public class Cat extends Animal{
public Cat(){
super(); // 默认的,写不写都有,默认就是找父类无参数构造器
System.out.println("==子类Cat无参数构造器被执行===");
}
public Cat(String n){
super(); // 默认的,写不写都有,默认就是找父类无参数构造器
System.out.println("==子类Cat有参数构造器被执行===");
}
}
public class Test {
public static void main(String[] args) {
Cat c = new Cat();
System.out.println("------------");
Cat c1 = new Cat("叮当猫");
}
}
super调用父类有参数构造器的作用:
如果父类中没有无参数构造器,只有有参构造器,会出现什么现象呢?
如何解决?
public class People {
private String name;
private int age;
public People() {
}
public People(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class Student extends People{
private String className;
public Student(){
}
public Student(String name, int age, String className) {
super(name, age);
this.className = className;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
}
public class Test {
public static void main(String[] args) {
Student s = new Student("张三", 21, "99期");
System.out.println(s.getName());
System.out.println(s.getAge());
System.out.println(s.getClassName());
}
}
关键字 | 访问成员变量 | 访问成员方法 | 访问构造方法 |
---|---|---|---|
this | this.成员变量 访问本类成员变量 | this.成员方法(…) 访问本类成员方法 | this(…)访问本类构器 |
super | super.成员变量 访问父类成员变量 | super.成员方法(…)访问父类成员方法 | super(…)访问父类构造器 |
在以上的总结中,唯独只有this调用本类其他构造器我们是没有接触过的。
this(…)访问本类构器实例:
public class Student {
private String schoolName;
private String name;
public Student(String name){
this(name , "黑马培训中心");
}
public Student(String name , String schoolName ){
this.name = name;
this.schoolName = schoolName;
}
}
什么是包?
导包
什么是权限修饰符?
权限修饰符的分类和具体作用范围:
权限修饰符:有四种作用范围由小到大(private->缺省->protected->public)
修饰符 | 同一个类中 | 同一个包中其他类 | 不同包下的子类 | 不同包下的无关类 |
---|---|---|---|---|
private | √ | |||
缺省 | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
学完权限修饰符需要具备如下能力
final的作用:
public class Test {
// 属于类,只加载一次,可以共享 (常量)
public static final String schoolName = "黑马";
public static final String schoolName2;
static{
schoolName2 = "传智";
// schoolName2 = "传智"; // 第二次赋值,报错了!
}
// 属于对象的! (final基本上不会用来修饰实例成员变量,没有意义!)
private final String name = "王麻子";
public static void main(String[] args) {
// final修饰变量,变量有且仅能被赋值一次。
/* 变量有几种:
局部变量。
成员变量。
-- 1、静态成员变量。
-- 2、实例成员变量。
*/
final int age;
age = 12;
// age = 20; // 第二次赋值,报错了!
System.out.println(age);
final double rate = 3.14;
buy(0.8);
// schoolName = "传智"; // 第二次赋值,报错了!
Test t = new Test();
// t.name = "麻子"; // 第二次赋值,报错了!
System.out.println(t.name);
}
public static void buy(final double z){
// z = 0.1; // 第二次赋值,报错了!
}
}
/**
final修饰类 类不能被继承了
*/
//final class Animal{
//}
//class Cat extends Animal{
//}
/**
final修饰方法,方法不能被重写
*/
class Animal{
public final void run(){
System.out.println("动物可以跑~~");
}
}
class Tiger extends Animal{
// @Override
// public void run() {
// System.out.println("老虎跑的贼快~~~");
// }
}
final修饰变量的注意:
public class Test2 {
public static void main(String[] args) {
// final修饰变量的注意事项:
// 1、final修饰基本类型变量,其数据不能再改变
final double rate = 3.14;
// rate = 3.15; // 第二次赋值,报错
// 2、final修饰引用数据类型的变量,变量中存储的地址不能被改变,但是地址指向的对象内容可以改变。
final int[] arr = {10, 20, 30};
System.out.println(arr);
// arr = null; // 属于第二次赋值,arr中的地址不能被改变
arr[1] = 200;
System.out.println(arr);
System.out.println(arr[1]);
}
常量
常量命名规范:英文单词全部大写,多个单词下划线连接起来。
public class Constant {
public static final String SCHOOL_NAME = "传智教育";
public static final String LOGIN_NAME = "admin";
public static final String PASS_WORD = "123456";
}
常量的执行原理:
案例说明:
选择常量做信息标志和分类:
代码部分涉及未学内容,无需过多关注
public class ConstantDemo2 {
public static final int UP = 1; // 上
public static final int DOWN = 2; // 上
public static final int LEFT = 3; // 左
public static final int RIGHT = 4; // 右
public static void main(String[] args) {
// 1、创建一个窗口对象(桌子)
JFrame win = new JFrame();
// 2、创建一个面板对象(桌布)
JPanel panel = new JPanel();
// 3、把桌布垫在桌子上
win.add(panel);
// 4、创建四个按钮对象
JButton btn1 = new JButton("上");
JButton btn2 = new JButton("下");
JButton btn3 = new JButton("左");
JButton btn4 = new JButton("右");
// 5、把按钮对象添加到桌布上去
panel.add(btn1);
panel.add(btn2);
panel.add(btn3);
panel.add(btn4);
// 6、显示窗口
win.setLocationRelativeTo(null);
win.setSize(300,400);
win.setVisible(true);
btn1.addActionListener(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
move(UP) ; // 让玛丽往上跳
}
});
btn2.addActionListener(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
move(ConstantDemo2.DOWN) ; // 让玛丽往下跳
}
});
btn3.addActionListener(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
move(LEFT) ; // 让玛丽往左跑
}
});
btn4.addActionListener(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
move(RIGHT) ; // 让玛丽往右跑
}
});
}
public static void move(int flag){
// 控制玛丽移动
switch (flag) {
case UP:
System.out.println("玛丽往上飞了一下~~");
break;
case DOWN:
System.out.println("玛丽往下蹲一下~~");
break;
case LEFT:
System.out.println("玛丽往左跑~~");
break;
case RIGHT:
System.out.println("玛丽往→跑~~");
break;
}
}
}
枚举的概述:
定义枚举类的格式:
修饰符 enum 枚举名称{
第一行都是罗列枚举类实例的名称。
}
enum Season{
SPRING , SUMMER , AUTUMN , WINTER;
}
反编译后观察枚举的特征:
Compiled from "Season.java"
public final class Season extends java.lang.Enum<Season> {
public static final Season SPRING = new Season();
public static final Season SUMMER = new Season();
public static final Season AUTUMN = new Season();
public static final Season WINTER = new Season();
public static Season[] values();
public static Season valueOf(java.lang.String);
}
枚举的特征:
案例说明:
选择常量做信息标志和分类:
枚举做信息标志和分类:
public enum Orientation {
UP, DOWN, LEFT, RIGHT;
}
public class EnumDemo2 {
public static void main(String[] args) {
// 1、创建一个窗口对象(桌子)
JFrame win = new JFrame();
// 2、创建一个面板对象(桌布)
JPanel panel = new JPanel();
// 3、把桌布垫在桌子上
win.add(panel);
// 4、创建四个按钮对象
JButton btn1 = new JButton("上");
JButton btn2 = new JButton("下");
JButton btn3 = new JButton("左");
JButton btn4 = new JButton("右");
// 5、把按钮对象添加到桌布上去
panel.add(btn1);
panel.add(btn2);
panel.add(btn3);
panel.add(btn4);
// 6、显示窗口
win.setLocationRelativeTo(null);
win.setSize(300,400);
win.setVisible(true);
btn1.addActionListener(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
move(Orientation.UP) ; // 让玛丽往上跳
}
});
btn2.addActionListener(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
move(Orientation.DOWN) ; // 让玛丽往下跳
}
});
btn3.addActionListener(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
move(Orientation.LEFT) ; // 让玛丽往左跑
}
});
btn4.addActionListener(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
move(Orientation.RIGHT) ; // 让玛丽往右跑
}
});
}
public static void move(Orientation o){
// 控制玛丽移动
switch (o) {
case UP:
System.out.println("玛丽往上飞了一下~~");
break;
case DOWN:
System.out.println("玛丽往下蹲一下~~");
break;
case LEFT:
System.out.println("玛丽往左跑~~");
break;
case RIGHT:
System.out.println("玛丽往→跑~~");
break;
}
}
}
抽象类:在Java中abstract是抽象的意思,如果一个类中的某个方法的具体实现不能确定,就可以申明成abstract修饰的抽象方法(不能写方法体了),这个类必须用abstract修饰,被称为抽象类。
public abstract class Animal{
public abstract void run();
}
抽象的使用总结与注意事项:
public abstract class Animal {
private String name;
public abstract void run();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Dog extends Animal{
@Override
public void run() {
System.out.println("狗跑很快");
}
}
public class Tiger extends Animal{
@Override
public void run() {
System.out.println("老虎跑很快");
}
}
public class Test {
public static void main(String[] args) {
Tiger t = new Tiger();
t.run();
Dog t1 = new Dog();
t1.run();
}
}
系统需求
分析实现
public abstract class Card {
private String name; // 主人名称
private double money;
public abstract void pay(double money);
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
}
public class GoldCard extends Card{
@Override
public void pay(double money) {
// 优惠后的金额算出来:
double rs = money * 0.8;
double lastMoney = getMoney() - rs;
System.out.println(getName() + "当前账户总金额:"
+ getMoney() +",当前消费了:" + rs +",消费后余额剩余:" +
lastMoney);
setMoney(lastMoney); // 更新账户对象余额
}
}
public class Test {
public static void main(String[] args) {
GoldCard c = new GoldCard();
c.setMoney(5000); // 父类的
c.setName("菜比");
c.pay(300);
System.out.println("余额:" + c.getMoney());
}
}
特征和注意事项:
final和abstract是什么关系?
使用场景说明:当系统中出现同一个功能多处在开发,而该功能中大部分代码是一样的,只有其中部分可能不同的时候。
模板方法模式实现步骤
案例:
需求:
分析:
public abstract class Account {
private String cardId;
private double money;
public Account() {
}
public Account(String cardId, double money) {
this.cardId = cardId;
this.money = money;
}
/**
模板方法
*/
public final void handle(String loginName , String passWord ){
// a.判断登录是否成功
if("abcdef".equals(loginName) && "123456".equals(passWord)){
System.out.println("登录成功。。");
// b.正式结算利息
// 当前模板方法知道所有子类账户都要结算利息,但是具体怎么结算,模板不清楚,交给具体的子类来计算
double result = calc();
// c.输出利息详情
System.out.println("本账户利息是:"+ result);
}else{
System.out.println("用户名或者密码错误了");
}
}
public abstract double calc();
public String getCardId() {
return cardId;
}
public void setCardId(String cardId) {
this.cardId = cardId;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
}
/**
活期账户
*/
public class CurrentAccount extends Account {
public CurrentAccount(String cardId, double money) {
super(cardId, money);
}
@Override
public double calc() {
// b.正式结算利息
double result = getMoney() * 0.0175; // 结算利息了
return result;
}
}
public class Test {
public static void main(String[] args) {
CurrentAccount acc = new CurrentAccount("ICBC-111", 100000);
acc.handle("abcdef", "123456");
}
}
模板方法我们是建议使用final修饰的,这样会更专业,那么为什么呢?
答:模板方法是给子类直接使用的,不是让子类重写的,一旦子类重写了模板方法就失效了。
模板方法模式解决了什么问题?
什么是接口:接口也是一种规范。
接口的定义与特点:
接口用关键字interface来定义
public interface 接口名 {
// 常量
// 抽象方法
}
public interface SportManInterface {
// 接口中的成员:JDK 1.8之前只有常量 和 抽象方法
// public static final 可以省略不写,接口默认会为你加上!
// public static final String SCHOOL_NAME = "菜园";
String SCHOOL_NAME = "菜园";
// 2、抽象方法
// public abstract 可以省略不写,接口默认会为你加上!
// public abstract void run();
void run();
// public abstract void eat();
void eat();
}
public class Test {
public static void main(String[] args) {
// 接口不能创建对象!
// SportManInterface s = new SportManInterface();
}
}
接口的用法:
修饰符 class 实现类 implements 接口1, 接口2, 接口3 , ... {
}
实现的关键字:implements
接口实现的注意事项:
public interface SportManInterface {
void run();
void competition();
}
public interface Law {
void rule();//遵章守法
}
public class PingPongMan implements SportManInterface, Law{
private String name;
public PingPongMan(String name) {
this.name = name;
}
@Override
public void rule() {
System.out.println(name + "要遵章守法,不能随意外出,酗酒,约会~~~");
}
@Override
public void run() {
System.out.println(name + "必须要跑步训练~~");
}
@Override
public void competition() {
System.out.println(name + "需要参加国际比赛~~");
}
}
基本小结
接口多继承的作用
public interface SportMan extends Law, People {
void run();
void competition();
}
public interface Law {
void rule(); // 遵章守法
void eat();
}
public interface People {
void eat();
}
/**
实现类
*/
// public class BasketballMan implements Law, SportMan, People {
public class BasketballMan implements SportMan{
@Override
public void rule() {
}
@Override
public void eat() {
}
@Override
public void run() {
}
@Override
public void competition() {
}
}
JDK8版本开始后,Java只对接口的成员方法进行了新增:
项目Version2.0需要对Inter接口丰富,加入10个新的抽象方法,此时改了接口就要所有实现类实现这些方法。(非常复杂)
新增:允许接口中直接定义带有方法体的方法
第一种:默认方法
default void run(){
System.out.println("--开始跑--");
}
第二种:静态方法
static void inAddr(){
System.out.println("我们都在学习Java新增方法的语法,它是Java源码自己会用到的~~~");
}
第三种:私有方法
private void go(){
System.out.println("--准备--");
}
实例:
public interface SportManInter {
/**
1、JDK 8开始 :默认方法(实例方法)
-- 必须default修饰,默认用public修饰
-- 默认方法,接口不能创建对象,这个方法只能过继给了实现类,由实现类的对象调用。
*/
default void run(){
go();
System.out.println("跑的很快~~~");
}
/**
2、静态方法
必须使用static修饰,默认用public修饰
-- 接口的静态方法,必须接口名自己调用。
*/
static void inAddr(){
System.out.println("我们都在学习Java新增方法的语法,它是Java源码自己会用到的~~~");
}
/**
3、私有方法(实例方法)
-- JDK 1.9开始才支持的。
-- 必须在接口内部才能被访问
*/
private void go(){
System.out.println("开始跑~~~");
}
}
class PingPongMan implements SportManInter{
}
class Test{
public static void main(String[] args) {
PingPongMan p = new PingPongMan();
p.run();
SportManInter.inAddr();
// PingPongMan.inAddr();
}
}
总结:JDK8开始后新增了那些方法?
注意:JDK8新增的3种方法我们自己在开发中很少使用,通常是Java源码涉及到的,我们需要理解、识别语法、明白调用关系即可。
interface AA{
default void go(){
System.out.println("AA");
}
}
interface BB{
default void go(){
System.out.println("BB");
}
}
class CC implements AA, BB{
@Override
public void go() {
}
}
interface AAA{
int run();
}
interface BBB{
void run();
}
interface CCC extends AAA, BBB{//报错
}
什么是多态?
多态的常见形式:
父类类型 对象名称 = new 子类构造器;
接口 对象名称 = new 实现类构造器;
多态中成员访问特点:
public class Animal {
public String name = "动物名称";
public void run(){
System.out.println("动物可以跑~~");
}
}
public class Dog extends Animal{
public String name = "狗名称";
@Override
public void run() {
System.out.println("狗跑的贼溜~~~~~");
}
}
public class Tortoise extends Animal{
public String name = "乌龟名称";
@Override
public void run() {
System.out.println("乌龟跑的非常慢~~~");
}
}
public class Test {
public static void main(String[] args) {
// 目标:先认识多态的形式
// 父类 对象名称 = new 子类构造器();
Animal a = new Dog();
a.run(); //狗 方法调用:编译看左,运行看右
System.out.println(a.name); // 变量调用:编译看左,运行也看左,动物名称
Animal a1 = new Tortoise();
a1.run();//乌龟
System.out.println(a1.name); // 动物名称
}
}
多态的前提
优势
Animal a = new Tortoise();
a.run(); // 后续业务行为随对象而变,后续代码无需修改
多态下会产生的一个问题: 多态下不能使用子类的独有功能。(下方解决)
自动类型转换(从子到父):子类对象赋值给父类类型的变量指向。
强制类型转换(从父到子):
- Java建议强转转换前使用instanceof判断当前对象的真实类型,再进行强制转换
- 变量名 instanceof 真实类型
判断关键字左边的变量指向的对象的真实类型,是否是右边的类型或者是其子类类型,是则返回true,反之false.
public class Animal {
public String name = "动物名称";
public void run(){
System.out.println("动物可以跑~~");
}
}
public class Dog extends Animal {
public String name = "狗名称";
@Override
public void run() {
System.out.println("跑的贼溜~~~~~");
}
/**
独有功能
*/
public void lookDoor(){
System.out.println("在看!!!");
}
}
public class Tortoise extends Animal {
public String name = "乌龟名称";
@Override
public void run() {
System.out.println("跑的非常慢~~~");
}
/**
独有功能
*/
public void layEggs(){
System.out.println("在下蛋~~~");
}
}
public class Test {
public static void main(String[] args) {
//自动类型转换
Animal a = new Dog();
a.run();
//a.lookDour; 自动类型转换无法调用子类独有功能
//强制类型转换可以调用子类独有功能
Dog d = (Dog)a;
d.lookDoor();
// 注意:多态下直接强制类型转换,可能出现类型转换异常
// 规定:有继承或者实现关系的2个类型就可以强制类型转换,运行时可能出现问题。
// Tortoise t1 = (Tortoise) a;
// 建议强制转换前,先判断变量指向对象的真实类型,再强制类型转换。
if(a instanceof Tortoise){
Tortoise t = (Tortoise) a;
t.layEggs();
}else if(a instanceof Dog){
Dog d1 = (Dog) a;
d1.lookDoor();
}
Animal a1 = new Tortoise();
go(a1);
}
public static void go(Animal a){
System.out.println("预备~~~");
a.run();
// 独有功能
if(a instanceof Tortoise){
Tortoise t = (Tortoise) a;
t.layEggs();
}else if(a instanceof Dog){
Dog d1 = (Dog) a;
d1.lookDoor();
}
System.out.println("结束~~~~");
}
}
需求:
分析
public interface USB {
void connected();
void unconnected();
}
public class Mouse implements USB{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Mouse() {
}
public Mouse(String name) {
this.name = name;
}
@Override
public void connected() {
System.out.println(name + "成功的接入了设备了~~~");
}
@Override
public void unconnected() {
System.out.println(name + "成功的从设备弹出了~~~");
}
/**
独有功能
*/
public void click(){
System.out.println(name + "双击点亮小红心~~~~");
}
}
public class KeyBoard implements USB{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public KeyBoard() {
}
public KeyBoard(String name) {
this.name = name;
}
@Override
public void connected() {
System.out.println(name + "成功的接入了设备了~~~");
}
@Override
public void unconnected() {
System.out.println(name + "成功的从设备弹出了~~~");
}
/**
独有功能
*/
public void keyDown(){
System.out.println(name + "写下了:老铁,6666,下次再来哦,老弟~~~~");
}
}
public class Computer {
/**
提供一个安装的入口:行为。
*/
public void installUSB(USB u){
u.connected();
if(u instanceof Mouse){
Mouse m = (Mouse) u;
m.click();
}else if(u instanceof KeyBoard){
KeyBoard k = (KeyBoard) u;
k.keyDown();
}
u.unconnected();
}
}
public class Test {
public static void main(String[] args) {
// a、创建电脑对象
Computer c = new Computer();
// b、创建USB设备对象
USB u = new Mouse("鼠标");
c.installUSB(u);
USB k = new KeyBoard("键盘");
c.installUSB(k);
}
}
内部类
public class People{
// 内部类
public class Heart{
}
}
内部类的使用场景、作用
内部类的分类
什么是静态内部类?
public class Outer{
// 静态成员内部类
public static class Inner{
}
}
静态内部类创建对象的格式:外部类名.内部类名 对象名 = new 外部类名.内部类构造器;
范例:Outer.Inner in = new Outer.Inner();
静态内部类的访问拓展:
1、静态内部类中是否可以直接访问外部类的静态成员?
2、静态内部类中是否可以直接访问外部类的实例成员?
什么是成员内部类?
public class Outer {
// 成员内部类
public class Inner {
}
}
成员内部类创建对象的格式:外部类名.内部类名 对象名 = new 外部类构造器.new 内部类构造器();
范例:Outer.Inner in = new Outer().new Inner();
成员内部类的访问拓展:
1、成员内部类中是否可以直接访问外部类的静态成员?
2、成员内部类的实例方法中是否可以直接访问外部类的实例成员?
局部内部类 (鸡肋语法,了解即可)
匿名内部类:
格式:
new 类|抽象类名|或者接口名() {
重写方法;
};
Animal a = new Animal() {
public void run() {
}
};
a. run();
特点总结:
public class Test {
public static void main(String[] args) {
Animal a = new Animal(){
@Override
public void run() {
System.out.println("老虎跑的块~~~");//相当于Animal的子类老虎
}
};
a.run();
}
}
某个学校需要让老师,学生,运动员一起参加游泳比赛
public class Test {
public static void main(String[] args) {
Swimming s = new Swimming() {
@Override
public void swim() {
System.out.println("快乐的游泳");
}
};
go(new Swimming() {
@Override
public void swim() {
System.out.println("老师游的很快");
}
});
}
public static void go(Swimming s){
System.out.println("开始");
s.swim();
System.out.println("结束");
}
}
interface Swimming{
void swim();
}
总结:匿名内部类可以作为方法的实际参数进行传输。
/**
目标:通过GUI编程 理解匿名内部类的真实使用场景
*/
public class Test3 {
public static void main(String[] args) {
// 1、创建窗口
JFrame win = new JFrame("登录界面");
JPanel panel = new JPanel();
win.add(panel);
// 2、创建一个按钮对象
JButton btn = new JButton("登录");
// 注意:讲解匿名内部类的使用
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(win, "点我一下,说明爱我!");
}
});
// btn.addActionListener( e -> JOptionPane.showMessageDialog(win, "别说话,吻我!!") );
// 3、把按钮对象添加到桌布上展示
panel.add(btn);
// 4、展示窗口
win.setSize(400, 300);
win.setLocationRelativeTo(null);
win.setVisible(true);
}
}
总结:开发中不是我们主动去定义匿名内部类的,而是别人需要我们写或者我们可以写的时候才会使用。
匿名内部类的代码可以实现代码进一步的简化(回扣主题)
Object类的作用:
Object类的常用方法:
方法名 | 说明 |
---|---|
public String toString() | 默认是返回当前对象在堆内存中的地址信息:类的全限名@内存地址 |
public boolean equals(Object o) | 默认是比较当前对象与另一个对象的地址是否相同,相同返回true,不同返回false |
问题引出
toString存在的意义
equals存在的意义
@Override
public boolean equals(Object o){
// 1、判断o是不是学生类型
if(o instanceof Student){
Student s2 = (Student) o;
// 2、判断2个对象的内容是否一样。
// if(this.name.equals(s2.name) &&
// this.age == s2.age && this.sex == s2.sex){
// return true;
// }else {
// return false;
// }
return this.name.equals(s2.name) && this.age == s2.age
&& this.sex == s2.sex ;
}else {
// 学生只能和学生比较,否则结果一定是false
return false;
}
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", sex=" + sex +
", age=" + age +
'}';
}
Objects概述:Objects是一个工具类,提供了一些方法去完成一些功能。
官方在进行字符串比较时,没有用字符串对象的的equals方法,而是选择了Objects的equals方法来比较。
@Override
public boolean equals(Object o) {
// 1、判断是否是同一个对象比较,如果是返回true。
if (this == o) return true;
// 2、如果o是null返回false 如果o不是学生类型返回false ...Student != ..Pig
if (o == null || this.getClass() != o.getClass()) return false;
// 3、说明o一定是学生类型而且不为null
Student student = (Student) o;
return sex == student.sex && age == student.age && Objects.equals(name, student.name);
}
使用Objects的equals方法在进行对象的比较会更安全。
Objects的常见方法:
方法名 | 说明 |
---|---|
public static boolean equals(Object a, Object b) | 比较两个对象的,底层会先进行非空判断,从而可以避免空指针异常。再进行equals比较 |
public static boolean isNull(Object obj) | 判断变量是否为null ,为null返回true ,反之false |
源码分析
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
对象进行内容比较的时候建议使用什么?为什么?
- 建议使用Objects提供的equals方法。
- 比较的结果是一样的,但是更安全。
StringBuilder概述
StringBuilder构造器
名称 | 说明 |
---|---|
public StringBuilder() | 创建一个空白的可变的字符串对象,不包含任何内容 |
public StringBuilder(String str) | 创建一个指定字符串内容的可变字符串对象 |
StringBuilder常用方法
方法名称 | 说明 |
---|---|
public StringBuilder append(任意类型) | 添加数据并返回StringBuilder对象本身 |
public StringBuilder reverse() | 将对象的内容反转 |
public int length() | 返回对象内容长度 |
public String toString() | 通过toString()就可以实现把StringBuilder转换为String |
生成String变量也是通过了StringBuilder,每次要产生两个对象。
只产生一个对象即可完成任务
总结:为什么拼接、反转字符串建议使用StringBuilder?
案例:打印整型数组内容
需求:
设计一个方法用于输出任意整型数组的内容,要求输出成如下格式:
“该数组内容为:[11, 22, 33, 44, 55]”
分析:
1、定义一个方法,要求该方法能够接收数组,并输出数组内容。 —> 需要参数吗?需要返回值类型申明吗?
2、定义一个静态初始化的数组,调用该方法,并传入该数组。
public class StringBuilderTest2 {
public static void main(String[] args) {
int[] arr1 = null;
System.out.println(toString(arr1));
int[] arr2 = {10, 88, 99};
System.out.println(toString(arr2));
int[] arr3 = {};
System.out.println(toString(arr3));
}
/**
1、定义方法接收任意整型数组,返回数组内容格式
*/
public static String toString(int[] arr){
if(arr != null){
// 2、开始拼接内容。
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < arr.length; i++) {
sb.append(arr[i] ).append(i == arr.length - 1 ? "" : ", ");
}
sb.append("]");
return sb.toString();
}else {
return null;
}
}
}
Math类
Math 类的常用方法
方法名 | 说明 |
---|---|
public static int abs(int a) | 获取参数绝对值 |
public static double ceil(double a) | 向上取整 |
public static double floor(double a) | 向下取整 |
public static int round(float a) | 四舍五入 |
public static int max(int a,int b) | 获取两个int值中的较大值 |
public static double pow(double a,double b) | 返回a的b次幂的值 |
public static double random() | 返回值为double的随机值,范围[0.0,1.0) |
System类概述:System也是一个工具类,代表了当前系统,提供了一些与系统相关的方法。
System 类的常用方法
方法名 | 说明 |
---|---|
public static void exit(int status) | 终止当前运行的 Java 虚拟机,非零表示异常终止 |
public static long currentTimeMillis() | 返回当前系统的时间毫秒值形式 |
public static void arraycopy(数据源数组, 起始索引, 目的地数组, 起始索引, 拷贝个数) | 数组拷贝 |
时间毫秒值
原因:
BigDecimal作用:用于解决浮点型运算精度失真的问题。
使用步骤:创建对象BigDecimal封装浮点型数据(最好的方式是调用方法)
BigDecima常用API
方法名 | 说明 |
---|---|
public BigDecimal add(BigDecimal b) | 加法 |
public BigDecimal subtract(BigDecimal b) | 减法 |
public BigDecimal multiply(BigDecimal b) | 乘法 |
public BigDecimal divide(BigDecimal b) | 除法 |
public BigDecimal divide (另一个BigDecimal对象,精确几位,舍入模式) | 除法 |
BigDecimal divide = bd1.divide(参与运算的对象, 小数点后精确到多少位, 舍入模式);
参数1 ,表示参与运算的BigDecimal 对象。
参数2 ,表示小数点后面精确到多少位
参数3 ,舍入模式
BigDecimal.ROUND_UP 进一法
BigDecimal.ROUND_FLOOR 去尾法
BigDecimal.ROUND_HALF_UP 四舍五入
面向对象进阶部分完结!