本文章参考B站 Java入门基础视频教程,java零基础自学首选黑马程序员Java入门教程(含Java项目和Java真题),仅供个人学习使用,部分内容为本人自己见解,与黑马程序员无关。
简单来说,面向对象就是找东西(找对象),设计东西(设计对象)过来解决问题(编程)
类
代码演示
public class Car {
// 属性(成员变量)
String name; // 名称
double price; // 价格
// 行为(方法)
public void start(){
System.out.println(name + " 价格是:" + price +", 启动成功!");
}
public void run(){
System.out.println(name + " 价格是:" + price +", 跑的很快!");
}
}
/**
目标:掌握自己设计类,并获得对象。
*/
public class Test {
public static void main(String[] args) {
// 如何去获取汽车的对象。
Car c1 = new Car();
System.out.println(c1);
c1.name = "宝马5系";
c1.price = 37.89;
System.out.println(c1.name);
System.out.println(c1.price);
c1.start();
c1.run();
System.out.println("-------------------");
Car c2 = new Car();
System.out.println(c2);
c2.name = "奔驰E系";
c2.price = 39.89;
System.out.println(c2.name);
System.out.println(c2.price);
c2.start();
c2.run();
}
}
输出结果:
com.itheima.createobject.Car@1b6d3586
宝马5系
37.89
宝马5系 价格是:37.89, 启动成功!
宝马5系 价格是:37.89, 跑的很快!
-------------------
com.itheima.createobject.Car@4554617c
奔驰E系
39.89
奔驰E系 价格是:39.89, 启动成功!
奔驰E系 价格是:39.89, 跑的很快!
多个对象的内存图
代码演示
public class Car {
// 属性(成员变量)
String name; // 名称
double price; // 价格
// 行为(方法)
public void start(){
System.out.println(name + " 价格是:" + price +", 启动成功!");
}
public void run(){
System.out.println(name + " 价格是:" + price +", 跑的很快!");
}
}
/**
目标:掌握自己设计类,并获得对象。
*/
public class Test {
public static void main(String[] args) {
// 如何去获取汽车的对象。
Car c1 = new Car();
System.out.println(c1);
c1.name = "宝马X3";
c1.price = 37.89;
System.out.println(c1.name);
System.out.println(c1.price);
c1.start();
c1.run();
System.out.println("-------------------");
Car c2 = new Car();
System.out.println(c2);
c2.name = "奔驰GLC";
c2.price = 39.89;
System.out.println(c2.name);
System.out.println(c2.price);
c2.start();
c2.run();
}
}
输出结果:
com.itheima.createobject.Car@1b6d3586
宝马X3
37.89
宝马X3 价格是:37.89, 启动成功!
宝马X3 价格是:37.89, 跑的很快!
-------------------
com.itheima.createobject.Car@4554617c
奔驰GLC
39.89
奔驰GLC 价格是:39.89, 启动成功!
奔驰GLC 价格是:39.89, 跑的很快!
两个变量指向同一个对象的内存图
代码演示
public class Student {
String name;
char sex;
String hobby;
public void study(){
System.out.println("姓名:" + name +",性别是:" + sex
+ ",爱好是:" + hobby + "的学生在好好学习,天天向上!");
}
}
public class Test {
public static void main(String[] args) {
// 目标:掌握2个变量指向同一个对象的形式
Student s1 = new Student();
s1.name = "小明";
s1.sex = '男';
s1.hobby = "睡觉、游戏、听课";
s1.study();
// 关键:把s1赋值给学生类型的变量s2
Student s2 = s1;
System.out.println(s1);
System.out.println(s2);
s2.hobby = "爱提问";
System.out.println(s2.name);
System.out.println(s2.sex);
System.out.println(s1.hobby);
s2.study();
s1 = null;
s2 = null;
System.out.println(s1.name);
}
}
输出结果:注意 s1.hobby 已经发生变化
姓名:小明,性别是:男,爱好是:睡觉、游戏、听课的学生在好好学习,天天向上!
com.itheima.memory.Student@1b6d3586
com.itheima.memory.Student@1b6d3586
小明
男
爱提问
姓名:小明,性别是:男,爱好是:爱提问的学生在好好学习,天天向上!
Exception in thread "main" java.lang.NullPointerException
at com.itheima.memory.Test.main(Test.java:26)
Process finished with exit code 1
数组Array和集合的区别:
补充:对象数组里面的每一个对象的值也是引用地址
通过构造器可以创建对象
代码演示
public class Car {
String name;
double price;
/**
无参数构造器(默认存在的)
*/
public Car(){
System.out.println("无参数构造器被触发执行~~~");
}
/**
有参数构造器
*/
public Car(String n, double p){
System.out.println("有参数构造器被触发执行~~~");
name = n;
price = p;
}
}
/**
目标:明白构造器的作用和分类。(开发的人,理解能力好)
*/
public class ConstructorDemo {
public static void main(String[] args) {
Car c = new Car();
// c.name = "";
// c.price
System.out.println(c.name);
System.out.println(c.price);
Car c2 = new Car("奔驰GLC", 39.78);
System.out.println(c2.name);
System.out.println(c2.price);
}
}
输出结果:
无参数构造器被触发执行~~~
null
0.0
有参数构造器被触发执行~~~
奔驰GLC
39.78
代码演示
package com.itheima.test;
public class Car {
private String name;
private double price;
public Car() {
System.out.println("无参构造器的this:" + this);
}
public void run() {
System.out.println("方法run中的this:" + this);
}
public Car(String name, Double price) {
System.out.println("有参构造器的this:" + this);
this.name = name;
this.price = price;
}
public void stop() {
System.out.println("方法stop中的this:" + this);
}
}
package com.itheima.test;
public class Test {
public static void main(String[] args) {
Car car = new Car();
System.out.println("car对象的地址:" + car);
car.run();
Car car1 = new Car("宝马5系", 48.88);
System.out.println("car1对象的地址:" + car1);
car1.stop();
}
}
输出结果:
无参构造器的this:com.itheima.test.Car@1b6d3586
car对象的地址:com.itheima.test.Car@1b6d3586
方法run中的this:com.itheima.test.Car@1b6d3586
有参构造器的this:com.itheima.test.Car@4554617c
car1对象的地址:com.itheima.test.Car@4554617c
方法stop中的this:com.itheima.test.Car@4554617c
没使用this的情况
public class Car {
String name;
double price;
public Car(String name, Double price) {
name = name;
price = price;
}
}
public class Test {
public static void main(String[] args) {
Car car = new Car("宝马五系", 48.88);
System.out.println(car.name);
System.out.println(car.price);
}
}
输出结果:
null
0.0
可以发现名字相同的话是就就近赋值,把传过来的值赋值给自己,并没有修改对象的属性值
加了 this 后效果演示
package com.itheima.test;
public class Car {
String name;
double price;
public Car(String name, Double price) {
this.name = name;
this.price = price;
}
}
package com.itheima.test;
public class Test {
public static void main(String[] args) {
Car car = new Car("宝马五系", 48.88);
System.out.println(car.name);
System.out.println(car.price);
}
}
输出结果:
宝马五系
48.88
this应用
public class Car {
String name;
double price;
public Car(String name, Double price) {
this.name = name;
this.price = price;
}
public void runWith(String name) {
System.out.println(name + "和" + name + "在比赛");
System.out.println(this.name + "和" + name + "在比赛");
}
}
public class Test {
public static void main(String[] args) {
Car car = new Car("宝马五系", 48.88);
car.runWith("奔驰E系");
}
}
奔驰E系和奔驰E系在比赛
宝马五系和奔驰E系在比赛
面向对象三大特征:封装、继承、多态
对象代表什么,就得封装对应的数据,并提供数据对应的行为
封装就是指将成员变量私有,提供方法暴露
封装的好处演示
public class Student {
// private私有的成员变量,只能在本类访问。
private int age;
public int getAge(){
return age;
}
public void setAge(int age){
if(age >= 0 && age <= 200){
this.age = age;
}else {
System.out.println("年龄数据有问题,应该不是人的年龄!");
}
}
}
/**
目标:学会面向对象的三大特征:封装的形式、作用。
*/
public class Test {
public static void main(String[] args) {
Student s = new Student();
// s.age = -23;
s.setAge(-23);
System.out.println(s.getAge());
}
}
输出结果:
年龄数据有问题,应该不是人的年龄!
0
String 类代表字符串,Java 程序中的所有字符串文字(例如“abc”)都被实现为此类的实例。也就是说,Java 程序中所有的双引号字符串,都是 String 类的对象。String 类在 java.lang 包下,所以使用的时候不需要导包!
String 对象创建出来是不会改变的
代码演示
/**
目标:String类创建字符串对象的2种方式
*/
public class StringDemo2 {
public static void main(String[] args) {
// 方式一:直接使用双引号得到字符串对象
String name = "我爱你中国";
System.out.println(name);
// 方式二:
// 1、public String(): 创建一个空白字符串对象,不含有任何内容 (几乎不用)
String s1 = new String(); // s1 = ""
System.out.println(s1);
// 2、public String(String): 根据传入的字符串内容,来创建字符串对象(几乎不用)
String s2 = new String("我是中国人");
System.out.println(s2);
// 3、public String(char[] c): 根据字符数组的内容,来创建字符串对象
char[] chars = {'a' , 'b' , '中', '国'};
String s3 = new String(chars);
System.out.println(s3);
// 4、public String(byte[] b): 根据字节数组的内容,来创建字符串对象
byte[] bytes = {97, 98, 99, 65, 66, 67};
String s4 = new String(bytes);
System.out.println(s4);
System.out.println("---------------------------------------");
String ss1 = "abc";
String ss2 = "abc";
System.out.println(ss1 == ss2);
char[] chars1 = {'a' , 'b' , 'c'};
String ss3 = new String(chars1);
String ss4 = new String(chars1);
System.out.println(ss3 == ss4);
}
}
输出结果:
我爱你中国
我是中国人
ab中国
abcABC
---------------------------------------
true
false
通过 “” 定义字符串内存原理
通过new构造器得到字符串对象内存原理
Java存在编译优化机制,程序在编译时: “a” + “b” + “c” 会直接转成 “abc”,原理可见编译后的 Class 文件
总结
总之,记住只有从常量池中取的才是相同的。
字符串比较用 “equals” 比较,不能直接用 “",因为 "” 是判断地址的
String xx = “xx” 在常量池中定义
用变量接收的字符串则在堆内存中
代码演示
public class StringAPIEqualsDemo4 {
public static void main(String[] args) {
// 1、正确登录名和密码
String okName = "itheima";
String okPassword = "123456";
// 2、请您输入登录名称和密码
Scanner sc = new Scanner(System.in);
System.out.println("登录名称:");
String name = sc.next();
System.out.println("登录密码:");
String password = sc.next();
// 3、判断用户输入的登录名称和密码与正确的内容是否相等。
if(okName.equals(name) && okPassword.equals(password)){
System.out.println("登录成功!");
}else {
System.out.println("用户名或者密码错误了!");
}
// 4、忽略大小写比较内容的Api: 一般用于比较验证码这样的业务逻辑
String sysCode = "23AdFh";
String code1 = "23aDfH";
System.out.println(sysCode.equals(code1)); // false
System.out.println(sysCode.equalsIgnoreCase(code1)); // true
}
}
输出结果:
登录名称:
itheima
登录密码:
123456
登录成功!
false
true
拓展
基本类型比较才用 “==”
代码演示
/**
目标:掌握String常用的其他API。
*/
public class StringAPIOtherDemo5 {
public static void main(String[] args) {
// 1、public int length(): 获取字符串的长度
String name = "我爱你中国love";
System.out.println(name.length());
// 2、public char charAt(int index): 获取某个索引位置处的字符
char c = name.charAt(1);
System.out.println(c);
System.out.println("------------遍历字符串中的每个字符--------------");
for (int i = 0; i < name.length(); i++) {
char ch = name.charAt(i);
System.out.print(ch + " ");
}
System.out.println();
System.out.println("------------字符串转换成字符数组--------------");
// 3、public char[] toCharArray():: 把字符串转换成字符数组
char[] chars = name.toCharArray();
for (int i = 0; i < chars.length; i++) {
char ch = chars[i];
System.out.print(ch + " ");
}
System.out.println();
System.out.println("------------字符串截取内容--------------");
// 4、public String substring(int beginIndex, int endIndex) :截取内容,(包前不包后的)
String name2 = "Java是最厉害的编程语言!";
// 01234567 89
String rs = name2.substring(0, 9);
System.out.println(rs);
String rs1 = name2.substring(4, 9);
System.out.println(rs1);
// 5、public String substring(int beginIndex):从当前索引一直截取到末尾
String rs2 = name2.substring(4);
System.out.println(rs2);
// 6、public String replace(CharSequence target, CharSequence replacement)
String name3 = "Chovy是最厉害的90后,Chovy棒棒的!我好爱Chovy";
String rs3 = name3.replace("Chovy", "***");
System.out.println(rs3);
// 7、public boolean contains(CharSequence s)
System.out.println(name3.contains("Chovy")); // true
System.out.println(name3.contains("Chovy1")); // false
// 8、public boolean startsWith(String prefix)
System.out.println(name3.startsWith("Chovy"));
System.out.println(name3.startsWith("Chovy是最厉害的"));
System.out.println(name3.startsWith("Chovy是最厉害的2"));
// 9、public String[] split(String s): 按照某个内容把字符串分割成字符串数组返回。
String name4 = "IU,TaylorSwift,Krystal";
String[] names = name4.split(",");
for (int i = 0; i < names.length; i++) {
System.out.println("选择了:" + names[i]);
}
}
}
输出结果:
9
爱
------------遍历字符串中的每个字符--------------
我 爱 你 中 国 l o v e
------------字符串转换成字符数组--------------
我 爱 你 中 国 l o v e
------------字符串截取内容--------------
Java是最厉害的
是最厉害的
是最厉害的编程语言!
***是最厉害的90后,***棒棒的!我好爱***
true
false
true
true
false
选择了:IU
选择了:TaylorSwift
选择了:Krystal
String类开发验证码功能
import java.util.Random;
/**
练习题:使用String完成随机生成5位的验证码。
*/
public class StringExec6 {
public static void main(String[] args) {
// 1、定义可能出现的字符信息
String datas = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
// 2、循环5次,每次生成一个随机的索引,提取对应的字符连接起来即可
String code = "";
Random r = new Random();
for (int i = 0; i < 5; i++) {
// 随机一个索引
int index = r.nextInt(datas.length());
char c = datas.charAt(index);
code += c;
}
// 3、输出字符串变量即可
System.out.println(code);
}
}
输出结果:
l6JUh
模拟用户登录功能
/**
练习题:模拟用户登录
*/
public class StringExec7 {
public static void main(String[] args) {
// 1、定义正确的登录名称和密码
String okLoginName = "admin";
String okPassword = "itheima";
// 2、定义一个循环,循环3次,让用户登录
Scanner sc = new Scanner(System.in);
for (int i = 1; i <= 3; i++) {
System.out.println("请您输入登录名称:");
String loginName = sc.next();
System.out.println("请您输入登录密码:");
String password = sc.next();
// 3、判断登录是否成功!
if(okLoginName.equals(loginName)){
// 4、判断密码是否正确
if(okPassword.equals(password)){
System.out.println("登录成功!欢迎进入系统随意浏览~~~~");
break;
}else {
// 密码错误了
System.out.println("您的密码不正确!您还剩余" + (3 - i) +"次机会登录!");
}
}else {
System.out.println("您的登录名称不正确!您还剩余" + (3 - i) +"次机会登录!");
}
}
}
}
输出结果:
请您输入登录名称:
admin1
请您输入登录密码:
123456
您的登录名称不正确!您还剩余2次机会登录!
请您输入登录名称:
admin
请您输入登录密码:
123456
您的密码不正确!您还剩余1次机会登录!
请您输入登录名称:
admin
请您输入登录密码:
itheima
登录成功!欢迎进入系统随意浏览~~~~
手机号码屏蔽
import java.util.Scanner;
/**
练习题:手机号码屏蔽
*/
public class StringExec8 {
public static void main(String[] args) {
// 1、键盘录入一个手机号码
Scanner sc = new Scanner(System.in);
System.out.println("请您输入您的手机号码:");
String tel = sc.next();
// 2、截取号码的前三位,后四位 18665666520
String before = tel.substring(0, 3); // 0 1 2
String after = tel.substring(7); // 从索引7开始截取到手机号码的末尾
String s = before + "****" + after;
System.out.println(s);
}
}
输出结果:
请您输入您的手机号码:
18665666520
186****6520
1、数组和集合的元素存储的个数问题?
2、数组和集合适合的场景
数组适合做数据个数和类型确定的场景
集合适合做数据个数不确定,且要做增删元素的场景
重点:数组跟 ArrayList 变量里面存的都是引用地址,只是获取的时候Java做了优化。
import java.util.ArrayList;
**
目标: 创建ArrayList对象,代表集合容器,往里面添加元素。
*/
public class ArrayListDemo1 {
public static void main(String[] args) {
// 1、创建ArrayList集合的对象
ArrayList list = new ArrayList();
// 2、添加数据
list.add("Java");
list.add("Java");
list.add("MySQL");
list.add("黑马");
list.add(23);
list.add(23.5);
list.add(false);
System.out.println(list.add('中'));
System.out.println(list);
// 3、给指定索引位置插入元素
list.add(1, "赵敏");
System.out.println(list);
}
}
输出结果:
true
[Java, Java, MySQL, 黑马, 23, 23.5, false, 中]
[Java, 赵敏, Java, MySQL, 黑马, 23, 23.5, false, 中]
import java.util.ArrayList;
/**
目标: 能够使用泛型约束ArrayList集合操作的数据类型
*/
public class ArrayListDemo2 {
public static void main(String[] args) {
// ArrayList list = new ArrayList();
ArrayList<String> list = new ArrayList<>(); // JDK 1.7开始,泛型后面的类型申明可以不写
list.add("Java");
list.add("MySQL");
// list.add(23);
// list.add(23.5);
ArrayList<Integer> list2 = new ArrayList<>();
list2.add(23);
list2.add(100);
// list2.add("Java");
}
}
泛型:
Java泛型是J2 SE1.5中引入的一个新特性,其本质是参数化类型,也就是说所操作的数据类型被指定为一个参数(type parameter)这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。
泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。
代码演示
import java.util.ArrayList;
/**
目标:掌握ArrayList集合的常用API
*/
public class ArrayListDemo3 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("Java");
list.add("Java");
list.add("MySQL");
list.add("MyBatis");
list.add("HTML");
// 1、public E get(int index):获取某个索引位置处的元素值
String e = list.get(3);
System.out.println(e);
// 2、public int size():获取集合的大小(元素个数)
System.out.println(list.size());
// 3、完成集合的遍历
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
// 4、public E remove(int index):删除某个索引位置处的元素值,并返回被删除的元素值
System.out.println(list); // [Java, Java, MySQL, MyBatis, HTML]
String e2 = list.remove(2);
System.out.println(e2);
System.out.println(list);
// 5、public boolean remove(Object o):直接删除元素值,删除成功返回true,删除失败返回false
System.out.println(list.remove("MyBatis"));
System.out.println(list);
ArrayList<String> list1 = new ArrayList<>();
list1.add("Java");
list1.add("C#");
list1.add("Java");
list1.add("MySQL");
System.out.println(list1);
// 只会删除第一次出现的这个元素值,后面的不删除
System.out.println(list1.remove("Java"));
System.out.println(list1);
// 6、public E set(int index,E element):修改某个索引位置处的元素值。
String e3 = list1.set(0 , "C++");
System.out.println(e3);
System.out.println(list1);
}
}
输出结果:
MyBatis
5
Java
Java
MySQL
MyBatis
HTML
[Java, Java, MySQL, MyBatis, HTML]
MySQL
[Java, Java, MyBatis, HTML]
true
[Java, Java, HTML]
[Java, C#, Java, MySQL]
true
[C#, Java, MySQL]
C#
[C++, Java, MySQL]
遍历并删除元素值(从后往前扫描)
import java.util.ArrayList;
/**
案例:从集合中遍历元素且删除。
*/
public class ArrayListTest4 {
public static void main(String[] args) {
// 1、创建集合对象:存入学生成绩(98,77,66,89,79,50,100)
ArrayList<Integer> scores = new ArrayList<>();
scores.add(98);
scores.add(77);
scores.add(66);
scores.add(89);
scores.add(79);
scores.add(50);
scores.add(100);
System.out.println(scores);
// [98, 77, 66, 89, 79, 50, 100]
// [98, 66, 89, 50, 100]
// i
// 1、遍历集合中的每个元素
// for (int i = 0; i < scores.size(); i++) {
// int score = scores.get(i);
// // 2、判断这个分数是否低于80分,如果低于则从集合中删除它
// if(score < 80){
// scores.remove(i); // 此处删完应该执行 i--
// }
// }
// System.out.println(scores);
// [98, 77, 66, 89, 79, 50, 100]
// [98, 89, 100]
// i
for (int i = scores.size() - 1; i >= 0 ; i--) {
int score = scores.get(i);
// 2、判断这个分数是否低于80分,如果低于则从集合中删除它
if(score < 80){
scores.remove(i);
}
}
System.out.println(scores);
}
}
输出结果:
[98, 77, 66, 89, 79, 50, 100]
[98, 89, 100]
注意:应该从后往前扫描,如果要从前往后扫描,在移除元素后应该执行 “i–”
存储自定义类型的对象 (结合数组)
System.out.println(movies);
// [com.itheima.arraylist.Movie@1b6d3586, com.itheima.arraylist.Movie@4554617c, com.itheima.arraylist.Movie@74a14482]
学生信息系统的数据搜索
public class Student {
private String studyId;
private String name;
private int age;
private String className;
public Student() {
}
public Student(String studyId, String name, int age, String className) {
this.studyId = studyId;
this.name = name;
this.age = age;
this.className = className;
}
public String getStudyId() {
return studyId;
}
public void setStudyId(String studyId) {
this.studyId = studyId;
}
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 String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
}
import java.util.ArrayList;
import java.util.Scanner;
/**
案例:学生信息系统:展示数据,并按照学号完成搜索
学生类信息(学号,姓名,性别,班级)
测试数据:
"20180302","叶孤城",23,"护理一班"
"20180303","东方不败",23,"推拿二班"
"20180304","西门吹雪",26,"中药学四班"
"20180305","梅超风",26,"神经科2班"
*/
public class ArrayListTest6 {
public static void main(String[] args) {
// 1、定义一个学生类,后期用于创建对象封装学生数据
// 2、定义一个集合对象用于装学生对象
ArrayList<Student> students = new ArrayList<>();
students.add(new Student("20180302","叶孤城",23,"护理一班"));
students.add(new Student("20180303","东方不败",23,"推拿二班"));
students.add(new Student( "20180304","西门吹雪",26,"中药学四班"));
students.add(new Student( "20180305","梅超风",26,"神经科2班"));
System.out.println("学号\t\t名称\t年龄\t\t班级");
// 3、遍历集合中的每个学生对象并展示其数据
for (int i = 0; i < students.size(); i++) {
Student s = students.get(i);
System.out.println(s.getStudyId() +"\t\t" + s.getName()+"\t\t"
+ s.getAge() +"\t\t" + s.getClassName());
}
// 4、让用户不断的输入学号,可以搜索出该学生对象信息并展示出来(独立成方法)
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请您输入要查询的学生的学号:");
String id = sc.next();
Student s = getStudentByStudyId(students, id);
// 判断学号是否存在
if(s == null){
System.out.println("查无此人!");
}else {
// 找到了该学生对象了,信息如下
System.out.println(s.getStudyId() +"\t\t" + s.getName()+"\t\t"
+ s.getAge() +"\t\t" + s.getClassName());
}
}
}
/**
根据学号,去集合中找出学生对象并返回。
* @param students
* @param studyId
* @return
*/
public static Student getStudentByStudyId(ArrayList<Student> students, String studyId){
for (int i = 0; i < students.size(); i++) {
Student s = students.get(i);
if(s.getStudyId().equals(studyId)){
return s;
}
}
return null; // 查无此学号!
}
}
输出结果:
学号 名称 年龄 班级
20180302 叶孤城 23 护理一班
20180303 东方不败 23 推拿二班
20180304 西门吹雪 26 中药学四班
20180305 梅超风 26 神经科2班
请您输入要查询的学生的学号:
20180302
20180302 叶孤城 23 护理一班
请您输入要查询的学生的学号:
20180307
查无此人!
请您输入要查询的学生的学号:
此项目可以深刻理解面向对象、参数传递、定义方法的好处
定义账户类
/**
系统的账户类,代表账户的信息
*/
public class Account {
private String cardId; // 卡号
private String userName; // 用户名称
private String passWord; // 密码
private double money; // 账户余额
private double quotaMoney; // 每次取现额度限度。
public String getCardId() {
return cardId;
}
public void setCardId(String cardId) {
this.cardId = cardId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassWord() {
return passWord;
}
public void setPassWord(String passWord) {
this.passWord = passWord;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
public double getQuotaMoney() {
return quotaMoney;
}
public void setQuotaMoney(double quotaMoney) {
this.quotaMoney = quotaMoney;
}
}
ATM 系统基本业务实现
import java.util.ArrayList;
import java.util.Random;
import java.util.Scanner;
public class ATMSystem {
public static void main(String[] args) {
// 2、创建一个集合对象,用于后期存入账户对象。
ArrayList<Account> accounts = new ArrayList<>();
// 3、展示系统欢迎页面
System.out.println("======欢迎您进入到黑马ATM系统===============");
while (true) {
System.out.println("1、登录账户");
System.out.println("2、注册账户");
System.out.println("请您选择操作:");
Scanner sc = new Scanner(System.in);
int command = sc.nextInt();
switch (command){
case 1:
// 登录操作
login(accounts, sc);
break;
case 2:
// 注册账户
register(accounts, sc);
break;
default:
System.out.println("当前输入的操作不存在!");
}
}
}
/**
用户登录功能
*/
private static void login(ArrayList<Account> accounts, Scanner sc) {
System.out.println("==================欢迎您进入到登录操作======================");
if(accounts.size() > 0){
while (true) {
System.out.println("请您输入登录的卡号:");
String cardId = sc.next();
// 2、根据卡号去集合中查询是否存在账户对象
Account acc = getAccountByCardId(cardId , accounts);
// 判断账户对象是否存在,存在说明卡号正确
if(acc != null){
while (true) {
// 3、判断密码是否正确
System.out.println("请您输入登录的密码:");
String passWord = sc.next();
if(acc.getPassWord().equals(passWord)){
// 登录成功了!
System.out.println("欢迎你:" + acc.getUserName() + "先生/女士进入系统,您可开始办理你的业务了!");
// 展示登录成功后的操作界面。
showCommand(sc, acc, accounts);
return;
}else {
System.out.println("您的密码不正确!");
}
}
}else {
System.out.println("卡号不存在,请确认!");
}
}
}else {
System.out.println("当前系统无任何账户,请先注册再登录!");
}
}
/**
登录后展示的界面
*/
private static void showCommand(Scanner sc, Account acc, ArrayList<Account> accounts) {
while (true) {
System.out.println("==================欢迎您进入到操作界面======================");
System.out.println("1、查询");
System.out.println("2、存款");
System.out.println("3、取款");
System.out.println("4、转账");
System.out.println("5、修改密码");
System.out.println("6、退出");
System.out.println("7、注销账户");
System.out.println("请您输入操作命令:");
int command = sc.nextInt();
switch (command) {
case 1:
// 查询账户信息展示
showAccount(acc);
break;
case 2:
// 存款
depositMoney(acc,sc);
break;
case 3:
// 取款
drawMoney(acc,sc);
break;
case 4:
// 转账
transferMoney(acc, accounts, sc);
break;
case 5:
// 修改密码
updatePassWord(acc, sc);
return; // 跳出当前操作的方法,最终回到首页
case 6:
// 退出 回到首页
System.out.println("欢迎下次继续光临!!");
return; // 结束登录后的全部操作
case 7:
// 注销账户
accounts.remove(acc); // 从集合对象中删除当前账户对象。
System.out.println("您的账户已经完成了销毁,您将不可以进行登录了!");
return;
default:
System.out.println("您的操作命令有误!");
}
}
}
/**
修改当前账户对象的密码
* @param acc
*/
private static void updatePassWord(Account acc, Scanner sc) {
// 1、判断旧密码是否正确
while (true) {
System.out.println("请您输入当前密码认证:");
String passWord = sc.next();
if(acc.getPassWord().equals(passWord)){
while (true) {
// 2、输入新密码
System.out.println("请您输入新密码:");
String newPassWord = sc.next();
System.out.println("请您确认新密码:");
String okPassWord = sc.next();
// 3、比对两次密码是否一致
if(newPassWord.equals(okPassWord)){
acc.setPassWord(okPassWord);
System.out.println("密码已经修改成功,请重新登录!");
return;
}else {
System.out.println("两次密码不一致!");
}
}
}else {
System.out.println("您输入的密码有误。请重新确认密码!");
}
}
}
/**
* 从当前账户对象中把金额转给其他账户对象。
* @param acc
* @param accounts
* @param sc
*/
private static void transferMoney(Account acc, ArrayList<Account> accounts, Scanner sc) {
// 1、判断自己的账户中是否有钱
if(acc.getMoney() <= 0){
System.out.println("您自己都没钱,就别转了吧!");
return;
}
// 2、判断总账户数量是否大于等于2个。
if(accounts.size() >= 2){
while (true) {
// 3、让当前用户输入对方的账号进行转账
System.out.println("请您输入对方卡号:");
String cardId = sc.next();
// 4、根据卡号查询出集合中的账户对象
Account otherAcc = getAccountByCardId(cardId , accounts);
// 5、判断账户对象是否存在,而且这个账户对象不能是自己。
if(otherAcc != null){
// 6、判断当前账户是否是自己。
if(acc.getCardId().equals(otherAcc.getCardId())){
System.out.println("不能给自己账户转账!");
}else {
// 7、正式进入到转账逻辑了
// 黑马刘德华
String rs = "*" + otherAcc.getUserName().substring(1);
System.out.println("请您确认["+rs +"]的姓氏来确认!");
System.out.println("请您输入对方的姓氏:");
String preName = sc.next();
if(otherAcc.getUserName().startsWith(preName)){
// 认证通过
while (true) {
System.out.println("请您输入转账的金额(您最多可以转账:" + acc.getMoney() +"元):");
double money = sc.nextDouble();
if(money > acc.getMoney()){
System.out.println("你不听话,没有这么多钱可以转!");
}else {
// 开始转
acc.setMoney(acc.getMoney() - money); // 更新自己账户
otherAcc.setMoney(otherAcc.getMoney() + money);
System.out.println("您已经完成转账!您当前还剩余:" + acc.getMoney());
return;
}
}
}else {
System.out.println("您输入对方的信息有误!");
}
}
}else {
System.out.println("您输入的转账卡号不存在!");
}
}
}else {
System.out.println("当前系统中没有其他账户可以转账,去注册一个账户吧!");
}
}
private static void drawMoney(Account acc, Scanner sc) {
System.out.println("==================欢迎进入账户取款操作======================");
// 1、判断账户的余额是否高于等于100
double money = acc.getMoney();
if(money >= 100) {
while (true) {
// 2、输入取钱的金额
System.out.println("请您输入取钱的金额:");
double drawMoney = sc.nextDouble();
// 3、判断取钱金额是否超过了当次限额
if(drawMoney > acc.getQuotaMoney()){
System.out.println("您当前取款金额超过了每次限额!");
}else {
// 4、判断当前取钱金额是超过了账户的余额
if(drawMoney > money){
System.out.println("当前余额不足!当前余额是:" + money);
}else {
// 更新账户余额
acc.setMoney(money - drawMoney);
System.out.println("您当前取钱完成,请拿走你的钱,当前剩余余额是:" + acc.getMoney());
break;
}
}
}
}else {
System.out.println("您当前账户余额不足100元,存钱去吧!");
}
}
private static void depositMoney(Account acc, Scanner sc) {
System.out.println("==================欢迎进入账户存款操作======================");
System.out.println("请您输入存款金额:");
double money = sc.nextDouble();
acc.setMoney(acc.getMoney() + money);
showAccount(acc);
}
private static void showAccount(Account acc) {
System.out.println("==================您当前账户详情信息如下======================");
System.out.println("卡号:" + acc.getCardId());
System.out.println("户主:" + acc.getUserName());
System.out.println("余额:" + acc.getMoney());
System.out.println("当次取现额度:" + acc.getQuotaMoney());
}
/**
开户功能
*/
private static void register(ArrayList<Account> accounts, Scanner sc) {
System.out.println("==================欢迎您进入到开户操作======================");
// 2、创建一个账户对象封装账户信息
Account acc = new Account();
// 1、录入用户账户信息
System.out.println("请您输入账户名称:");
String userName =sc.next();
acc.setUserName(userName);
while (true) {
System.out.println("请您输入账户密码:");
String passWord =sc.next();
System.out.println("请您输入确认密码:");
String okPassWord =sc.next();
if(okPassWord.equals(passWord)){
// 密码无问题
acc.setPassWord(okPassWord);
break;
}else {
System.out.println("两次输入的密码不一致!");
}
}
System.out.println("请您设置当次取现额度:");
double quataMoney = sc.nextDouble();
acc.setQuotaMoney(quataMoney);
// 关键点:为当前账户生成一个随机的8位数字作为卡号,卡号不能与其他用户的卡号重复。
String cardId = createCardId(accounts);
acc.setCardId(cardId);
// 3、把账户对象存入到集合容器对象中去
accounts.add(acc);
System.out.println("恭喜您,"+acc.getUserName()+"先生/女士,您开户完成,您的卡号是:" + acc.getCardId());
}
public static String createCardId(ArrayList<Account> accounts){
while (true) {
String cardId = "";
// 随机8个数字
Random r = new Random();
for (int i = 1; i <= 8 ; i++) {
cardId += r.nextInt(10);
}
// 判断这个卡号是否重复:根据卡号去查询账户对象
Account account = getAccountByCardId(cardId , accounts);
if(account == null){
return cardId;
}
}
}
public static Account getAccountByCardId(String cardId , ArrayList<Account> accounts){
for (int i = 0; i < accounts.size(); i++) {
Account acc = accounts.get(i);
if(acc.getCardId().equals(cardId)){
return acc;
}
}
return null;
}
}
static关键字的作用
static 是静态的意思,可以修饰成员变量和成员方法。
static 修饰成员变量表示该成员变量只在内存中只存储一份,可以被共享访问、修改。
非 static 修饰成员变量是属于对象的,需要创建对象才能使用
成员变量
代码演示
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、对象.实例成员变量
// System.out.println(name);
User u1 = new User();
u1.name = "猪八戒";
u1.age = 36;
System.out.println(u1.name);
System.out.println(u1.age);
// 对象.静态成员变量(不推荐这样访问)
u1.onLineNumber++;
User u2 = new User();
u2.name = "孙悟空";
u2.age = 38;
System.out.println(u2.name);
System.out.println(u2.age);
// 对象.静态成员变量(不推荐这样访问)
u2.onLineNumber++;
System.out.println(onLineNumber);
}
}
输出结果:
162
猪八戒
36
孙悟空
38
164
内存原理:
总结
静态成员变量(有static修饰,属于类、加载一次,可以被共享访问)
实例成员变量(无static修饰,属于对象):
静态成员变量:表示在线人数等需要被共享的信息。
实例成员变量:属于每个对象,且每个对象信息不同时(name,age,…等)
成员方法的分类
静态成员方法(有static修饰,属于类),建议用类名访问,也可以用对象访问。
实例成员方法(无static修饰,属于对象),只能用对象触发访问。
使用场景
表示对象自己的行为的,且方法中需要访问实例成员的,则该方法必须申明成实例方法。
如果该方法是以执行一个共用功能为目的,则可以申明成静态方法。
代码演示
public class Student {
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、类名.静态方法
Student.getMax(10, 100);
// 注意:同一个类中访问静态成员 可以省略类名不写
getMax(200, 20);
// 2、对象.实例方法
// study(); // 报错的
Student s = new Student();
s.name = "全蛋儿";
s.study();
// 3、对象.静态方法(不推荐)
s.getMax(300,20);
}
}
输出结果:
100
200
全蛋儿在好好学习,天天向上~~
300
内存原理
验证码工具类
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 Login {
public static void main(String[] args) {
// 验证码:
System.out.println("验证码:" + VerifyTool.createCode(10));
}
}
public class Register {
public static void main(String[] args) {
// 验证码:
System.out.println("验证码:" + VerifyTool.createCode(5));
}
}
数组工具类
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));
}
}
输出结果:
[I@1b6d3586
[10, 20, 30]
20.0
null
[]
工具类的定义注意
建议将工具类的构造器进行私有,工具类无需创建对象。
里面都是静态方法,直接用类名访问即可。
代码块概述
代码块是类的5大成分之一(成员变量、构造器,方法,代码块,内部类),定义在类中方法外。
在Java类下,使用 { } 括起来的代码被称为代码块
代码块分类
格式:static{}
特点:需要通过static关键字修饰,随着类的加载而加载,并且自动触发、只执行一次
使用场景:在类加载的时候做一些静态数据初始化的操作,以便后续使用。
格式:{}
特点:每次创建对象,调用构造器执行时,都会执行该代码块中的代码,并且在构造器执行前执行
使用场景:初始化实例资源。
静态代码块作用
如果要在启动系统时对数据进行初始化。
建议使用静态代码块完成数据的初始化操作,代码优雅。
静态代码块优先执行
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方法被执行输出===========
黑马程序员
静态代码块应用
import java.util.ArrayList;
public class StaticCodeTest3 {
/**
模拟初始化牌操作
点数: "3","4","5","6","7","8","9","10","J","Q","K","A","2"
花色: "♠", "♥", "♣", "♦"
1、准备一个容器,存储54张牌对象,这个容器建议使用静态的集合。静态的集合只加载一次。
*/
// int age = 12;
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);
}
}
输出结果:
新牌:[3♠, 3♥, 3♣, 3♦, 4♠, 4♥, 4♣, 4♦, 5♠, 5♥, 5♣, 5♦, 6♠, 6♥, 6♣, 6♦, 7♠, 7♥, 7♣, 7♦, 8♠, 8♥, 8♣, 8♦, 9♠, 9♥, 9♣, 9♦, 10♠, 10♥, 10♣, 10♦, J♠, J♥, J♣, J♦, Q♠, Q♥, Q♣, Q♦, K♠, K♥, K♣, K♦, A♠, A♥, A♣, A♦, 2♠, 2♥, 2♣, 2♦, 小, 大]
构造代码块(实例代码块)
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);
}
}
输出结果
==构造代码块被触发执行一次==
==构造器被触发执行==
张麻子
==构造代码块被触发执行一次==
==构造器被触发执行==
张麻子
饿汉式
/**
目标:学会使用恶汉单例模式设计单例类
*/
public class SingleInstance1 {
/**
static修饰的成员变量,静态成员变量,加载一次,只有一份
*/
// public static int onLineNumber = 21;
public static SingleInstance1 instance = new SingleInstance1();
/**
1、必须私有构造器:私有构造器对外不能被访问。
*/
private SingleInstance1(){
}
}
public class Test {
public static void main(String[] args) {
// SingleInstance1 s1 = new SingleInstance1();
// SingleInstance1 s2 = new SingleInstance1();
// SingleInstance1 s3 = new SingleInstance1();
SingleInstance1 s1 = SingleInstance1.instance;
SingleInstance1 s2 = SingleInstance1.instance;
SingleInstance1 s3 = SingleInstance1.instance;
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
System.out.println(s1 == s2);
}
}
输出结果:
com.itheima.d6_singleinstance.SingleInstance1@1b6d3586
com.itheima.d6_singleinstance.SingleInstance1@1b6d3586
com.itheima.d6_singleinstance.SingleInstance1@1b6d3586
true
懒汉式(线程不安全)
挖坑:后面会具体分析
/**
目标:设计懒汉单例
*/
public class SingleInstance2 {
/**
2、定义一个静态的成员变量用于存储一个对象,一开始不要初始化对象,因为人家是懒汉
*/
private static SingleInstance2 instance;
/**
1、私有构造器啊
*/
private SingleInstance2(){
}
/**
3、提供一个方法暴露,真正调用这个方法的时候才创建一个单例对象
*/
public static SingleInstance2 getInstance(){
if(instance == null){
// 第一次来拿对象,为他做一个对象
instance = new SingleInstance2();
}
return instance;
}
}
public class Test2 {
public static void main(String[] args) {
// 得到一个对象
SingleInstance2 s1 = SingleInstance2.getInstance();
SingleInstance2 s2 = SingleInstance2.getInstance();
System.out.println(s1 == s2);
}
}
输出结果:
true
静态方法只能访问静态的成员,不可以直接访问实例成员。
实例方法可以访问静态的成员,也可以访问实例成员。
静态方法中是不可以出现this关键字的。
总结:总之,记住静态方法无需创建对象就能使用,一切就很好理解了。
什么是继承?
Java中提供一个关键字extends,用这个关键字,我们可以让一个类和另一个类建立起父子关系。
Student称为子类(派生类),People称为父类(基类 或超类)。
使用继承的好处
继承展示
内存原理
继承的特点
注意事项
1、子类是否可以继承父类的构造器?
2、子类是否可以继承父类的私有成员?
3、子类是否可以继承父类的静态成员?
4、在子类方法中访问成员(成员变量、成员方法)满足:就近原则
先子类局部范围找
然后子类成员范围找
然后父类成员范围找,如果父类范围还没有找到则报错
补充:
成员变量:
1、成员变量定义在类中,在整个类中都可以被访问。
2、成员变量随着对象的建立而建立,随着对象的消失而消失,存在于对象所在的堆内存中。
3、成员变量有默认初始化值。
局部变量:
1、局部变量只定义在局部范围内,如:函数内,语句内等,只在所属的区域有效。
2、局部变量存在于栈内存中,作用的范围结束,变量空间会自动释放。
3、局部变量没有默认初始化值
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 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 Test {
public static void main(String[] args) {
NewPhone huawei = new NewPhone();
huawei.call();
huawei.sendMessage();
}
}
输出结果:
打电话开始~~~
支持视频通话~~~
发送短信开始~~~
支持发送图片和视频~~~
@Override重写注解
@Override是放在重写后的方法上,作为重写是否正确的校验注解。
加上该注解后如果重写错误,编译阶段会出现错误提示。
建议重写方法都加@Override注解,代码安全,优雅!
方法重写注意事项和要求
重写方法的名称、形参列表必须与被重写方法的名称和参数列表一致。
私有方法不能被重写。
子类重写父类方法时,访问权限必须大于或者等于父类 (暂时了解 :缺省 < protected < public)
子类不能重写父类的静态方法,如果重写会报错的
开发中一般重写方法的名称、形参列表,访问权限和被重写方法保持一致,只是更改方法里面内容
特点
子类中所有的构造器默认都会先访问父类中无参的构造器,再执行自己。
为什么?
子类在初始化的时候,有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据。
子类初始化之前,一定要调用父类构造器先完成父类数据空间的初始化。
怎么调用父类构造器的?
代码演示
父类
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("叮当猫");
}
}
输出结果:
==父类Animal无参数构造器被执行===
==子类Cat无参数构造器被执行===
------------
==父类Animal无参数构造器被执行===
==子类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());
}
}
输出结果
张三
21
99期
this() 访问本类构造器案例分析
this(…)和super(…)使用注意点:
子类通过 this (…)去调用本类的其他构造器,本类其他构造器会通过 super 去手动调用父类的构造器,最终还是会调用父类构造器的。
注意:this(…) super(…) 都只能放在构造器的第一行,所以二者不能共存在同一个构造器中。
导包
什么是权限修饰符?
权限修饰符的分类和具体作用范围
自己定义成员(方法,成员变量,构造器等)一般满足如下要求
在子类中应该用子类对象,而不是用父类对象去访问父类对象中的 protected 方法
public class Fu {
// 1.private 只能本类中访问
private void show1() {
System.out.println("private");
}
// 2.缺省:本类,同一个包下的类中。
void show2() {
System.out.println("缺省");
}
// 3.protected:本类,同一个包下的类中,其他包下的子类
protected void show3() {
System.out.println("protected");
}
// 4.任何地方都可以
public void show4() {
System.out.println("public");
}
public static void main(String[] args) {
//创建Fu的对象,测试看有哪些方法可以使用
Fu f = new Fu();
f.show1();
f.show2();
f.show3();
f.show4();
}
}
public class Zi extends Fu {
public static void main(String[] args) {
//创建Zi的对象,测试看有哪些方法可以使用
Zi z = new Zi();
z.show3();
z.show4();
}
}
final的作用
final 关键字是最终的意思,可以修饰(方法,变量,类)
修饰方法:表明该方法是最终方法,不能被重写。
修饰变量:表示该变量第一次赋值后,不能再次被赋值(有且仅能被赋值一次)。
修饰类:表明该类是最终类,不能被继承。
工具类一般可以使用 Final 修饰
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);
System.out.println(arr[1]);
// arr = null; // 属于第二次赋值,arr中的地址不能被改变
arr[1] = 200;
System.out.println(arr);
System.out.println(arr[1]);
}
}
[I@1b6d3586
20
[I@1b6d3586
200
参考链接
https://www.cnblogs.com/cainiao-chuanqi/p/11073993.html
1.定义的位置不一样【重点】
2.作用范围不一样【重点】
3.默认值不一样【重点】
4.内存的位置不一样(了解)
5.生命周期不一样(了解)
概述
常量的执行原理
常量做信息标志和分类
import javax.swing.*;
import java.awt.event.ActionEvent;
/**
目标: 常量的其他作用,做信息标志和信息分类(其实也是一种配置形式)
*/
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;
}
}
}
概述
反编译后观察枚举的特征
枚举改进超级玛丽代码
/**
做信息标志和分类
*/
public enum Orientation {
UP, DOWN, LEFT, RIGHT;
}
import javax.swing.*;
import java.awt.event.ActionEvent;
/**
目标: 常量的其他作用,做信息标志和信息分类(其实也是一种配置形式)
*/
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;
}
}
}
选择常量做信息标志和分类
枚举做信息标志和分类
拓展:向枚举中添加新方法
public enum Color {
RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);
// 成员变量
private String name;
private int index;
// 构造方法
private Color(String name, int index) {
this.name = name;
this.index = index;
}
// 普通方法
public static String getName(int index) {
for (Color c : Color.values()) {
if (c.getIndex() == index) {
return c.name;
}
}
return null;
}
// get set 方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
}
测试
public class TestColor {
public static void main(String[] args) {
System.out.println(Color.getName(1));
System.out.println(Color.getName(2));
System.out.println(Color.getName(3));
System.out.println(Color.getName(4));
Color.RED.setName("红红火火恍恍惚惚");
System.out.println(Color.RED.getName());
}
}
输出内容
红色
绿色
白色
黄色
红红火火恍恍惚惚
在Java中abstract是抽象的意思,如果一个类中的某个方法的具体实现不能确定,就可以申明成abstract修饰的抽象方法(不能写方法体了),这个类必须用abstract修饰,被称为抽象类。
抽象的使用总结与注意事项
抽象类的作用是什么样的?
抽象方法是什么样的?
继承抽象类有哪些要注意?
抽象父类
/**
抽象父类
*/
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(10000); // 父类的
c.setName("三石");
c.pay(300);
System.out.println("余额:" + c.getMoney());
}
}
输出结果
三石当前账户总金额:10000.0,当前消费了:240.0,消费后余额剩余:9760.0
余额:9760.0
抽象类为什么不能创建对象?
如果抽象类可以创建对象,那么就可以调用里面的抽象方法,方法还没被实现就被调用,程序就崩了。
互斥关系
使用场景
模板方法模式实现步骤
案例:银行利息结算系统
需求:
使用模板方法模式前
/**
活期账户
*/
public class CurrentAccount {
private String cardId;
private double money;
public CurrentAccount() {
}
public CurrentAccount(String cardId, double money) {
this.cardId = cardId;
this.money = money;
}
/**
登录结算利息
*/
public void handle(String loginName , String passWord ){
// a.判断登录是否成功
if("itheima".equals(loginName) && "123456".equals(passWord)){
System.out.println("登录成功。。");
// b.正式结算利息
double result = money * 0.0175; // 结算利息了
// c.输出利息详情
System.out.println("本账户利息是:"+ result);
}else{
System.out.println("用户名或者密码错误了");
}
}
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 FixedAccount {
private String cardId;
private double money;
public FixedAccount() {
}
public FixedAccount(String cardId, double money) {
this.cardId = cardId;
this.money = money;
}
/**
登录结算
*/
public void handle(String loginName , String passWord ){
// a.判断登录是否成功
if("itheima".equals(loginName) && "123456".equals(passWord)){
System.out.println("登录成功。。");
// b.正式结算利息
double result = money * 0.035; // 结算利息了
if(money >= 100000){
result += money * 0.03;
}
// c.输出利息详情
System.out.println("本账户利息是:"+ result);
}else{
System.out.println("用户名或者密码错误了");
}
}
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 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("itheima".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;
}
}
板方法我们是建议使用final修饰的,这样会更专业,那么为什么呢?
模板方法是给子类直接使用的,不是让子类重写的,一旦子类重写了模板方法就失效了。
模板方法模式解决了什么问题?
/**
接口
*/
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();
}
接口的用法
接口是用来被类实现(implements)的,实现接口的类称为实现类。实现类可以理解成所谓的子类。
public class PingPongMan implements SportMan , Law{
@Override
...
}
从上面可以看出,接口可以被类单实现,也可以被类多实现。
接口实现的注意事项
一个类实现接口,必须重写完全部接口的全部抽象方法,否则这个类需要定义成抽象类。
基本小结
接口多继承的作用
接口1
public interface Law {
void rule(); // 遵章守法
void eat();
}
接口2
public interface People {
void eat();
}
接口3
public interface SportMan extends Law, People {
void run();
void competition();
}
实现类
/**
实现类
*/
// 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() {
}
}
1、接口不能创建对象
2、一个类实现多个接口,多个接口中有同样的静态方法不冲突。
3、一个类继承了父类,同时又实现了接口,父类中和接口中有同名方法,默认用父类的。
4、一个类实现了多个接口,多个接口中存在同名的默认方法,不冲突,这个类重写该方法即可。
5、一个接口继承多个接口,是没有问题的,如果多个接口中存在规范冲突则不能多继承。
多态
方法调用:编译看左边,运行看右边。
变量调用:编译看左边,运行也看左边。(多态侧重行为多态)
父类
/**
父类
*/
public class Animal {
public String name = "动物名称";
public void run(){
System.out.println("动物可以跑~~");
}
}
子类1
public class Dog extends Animal{
public String name = "狗名称";
@Override
public void run() {
System.out.println("跑的贼溜~~~~~");
}
}
子类2
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); // 动物名称
}
}
输出结果
跑的贼溜~~~~~
动物名称
跑的非常慢~~~
动物名称
什么是多态?
多态的常见形式
父类类型 对象名称 = new 子类构造器;
接口 对象名称 = new 实现类构造器;
多态中成员访问特点
多态的前提
优势
在多态形式下,右边对象可以实现解耦合,便于扩展和维护。
Animal a = new Dog();
a.run(); // 后续业务行为随对象而变,后续代码无需修改
定义方法的时候,使用父类型作为参数,该方法就可以接收这父类的一切子类对象,体现出多态的扩展性与便利。
自动类型转换(从子到父):
强制类型转换(从父到子)
多态下引用数据类型的类型转换
Animal a = new Dog();
a.run();
//a.lookDoor(); // 多态下无法调用子类独有功能
// 强制类型转换:可以实现调用子类独有功能的
Dog d = (Dog) a;
d.lookDoor();
如果转型后的类型和对象真实类型不是同一种类型,那么在转换的时候就会出现ClassCastException
Animal t = new Tortoise();
Dog d = (Dog)t; // 出现异常 ClassCastException
Java建议强转转换前使用 instanceof 判断当前对象的真实类型,再进行强制转换
// 建议强制转换前,先判断变量指向对象的真实类型,再强制类型转换。
if(a instanceof Tortoise){
Tortoise t = (Tortoise) a;
t.layEggs();
}else if(a instanceof Dog){
Dog d1 = (Dog) a;
d1.lookDoor();
}
案例
需求:
分析:
usb接口
public interface USB {
void connect();
void unconnect();
}
鼠标类
/**
实现类(子类)
*/
public class Mouse implements USB{
private String name;
public Mouse(String name) {
this.name = name;
}
@Override
public void connect() {
System.out.println(name + "成功的接入了设备了~~~");
}
@Override
public void unconnect() {
System.out.println(name + "成功的从设备弹出了~~~");
}
/**
独有功能
*/
public void click(){
System.out.println(name + "双击点亮小红心~~~~");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
键盘类
/**
实现类(子类)
*/
public class KeyBoard implements USB{
private String name;
public KeyBoard(String name) {
this.name = name;
}
@Override
public void connect() {
System.out.println(name + "成功的接入了设备了~~~");
}
@Override
public void unconnect() {
System.out.println(name + "成功的从设备弹出了~~~");
}
/**
独有功能
*/
public void keyDown(){
System.out.println(name + "写下了:老铁,6666,下次再来哦,老弟~~~~");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
电脑类
public class Computer {
/**
提供一个安装的入口:行为。
*/
public void installUSB(USB u){
u.connect();
// 独有功能
if(u instanceof Mouse){
Mouse m = (Mouse) u;
m.click();
}else if(u instanceof KeyBoard) {
KeyBoard k = (KeyBoard) u;
k.keyDown();
}
u.unconnect();
}
}
测试类
/**
目标:USB设备模拟
1、定义USB接口:接入 拔出
2、定义2个USB的实现类:鼠标、键盘。
3、创建一个电脑对象,创建USB设备对象,安装启动。
*/
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);
}
}
输出结果
罗技鼠标成功的接入了设备了~~~
罗技鼠标双击点亮小红心~~~~
罗技鼠标成功的从设备弹出了~~~
双飞燕键盘成功的接入了设备了~~~
双飞燕键盘写下了:老铁,6666,下次再来哦,老弟~~~~
双飞燕键盘成功的从设备弹出了~~~
内部类
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、成员内部类的实例方法中是否可以直接访问外部类的实例成员?
面试
class People{
private int heartbeat = 150;
/**
成员内部类
*/
public class Heart{
private int heartbeat = 110;
public void show(){
int heartbeat = 78;
System.out.println(heartbeat); // 78
System.out.println(this.heartbeat); // 110
System.out.println(People.this.heartbeat); // 150
}
}
}
局部内部类 (鸡肋语法,了解即可)
其实就是在方法里写一个类,这种做法我们开发是尽量避免使用的。
匿名内部类
// 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("老虎跑的块~~~");
}
};
a.run();
}
}
//class Tiger extends Animal{
// @Override
// public void run() {
// System.out.println("老虎跑的块~~~");
// }
//}
abstract class Animal{
public abstract void run();
}
输出结果
老虎跑的块~~~
需求:某个学校需要让老师,学生,运动员一起参加游泳比赛
/**
目标:掌握匿名内部类的使用形式(语法)
*/
public class Test2 {
public static void main(String[] args) {
Swimming s = new Swimming() {
@Override
public void swim() {
System.out.println("学生快乐的自由泳");
}
};
go(s);
System.out.println("--------------");
Swimming s1 = new Swimming() {
@Override
public void swim() {
System.out.println("老师泳的贼快~~~~~");
}
};
go(s1);
System.out.println("--------------");
go(new Swimming() {
@Override
public void swim() {
System.out.println("运动员的贼快啊~~~~~");
}
});
// 简化写法
// go(() -> System.out.println("运动员的贼快啊~~~~~"));
}
/**
学生 老师 运动员可以一起参加游泳比赛
*/
public static void go(Swimming s){
System.out.println("开始。。。");
s.swim();
System.out.println("结束。。。");
}
}
interface Swimming{
void swim();
}
输出结果
开始。。。
学生快乐的自由泳
结束。。。
--------------
开始。。。
老师泳的贼快~~~~~
结束。。。
--------------
开始。。。
运动员的贼快啊~~~~~
结束。。。
总结
匿名内部类可以作为方法的实际参数进行传输。
简化写法(提前理解)
go(new Swimming() {
@Override
public void swim() {
System.out.println("运动员的贼快啊~~~~~");
}
});
// 简化写法
// go(() -> System.out.println("运动员的贼快啊~~~~~"));
代码演示
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
目标:通过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);
}
}
总结
开发中不是我们主动去定义匿名内部类的,而是别人需要我们写或者我们可以写的时候才会使用。匿名内部类的代码可以实现代码进一步的简化