零、IDEA集成开发环境
0.1 IDEA的使用
- 在IDEA中,选择 File-New-Project 创建一个工程
- 工程命名并为其选择工程目录,点击Finish
- 弹出项目结构,点击上面的+号,选择New Module
- 选择Java,点击Next,为Module命名,点击Finish,点击OK
- 模块创建完成,在模块中的src中新建Java类
0.2 快捷键
快速生成main方法 | psvm或main |
---|---|
快速生成输出语句 | sout |
删除一行 | Ctrl +Y |
左侧列表移动开合 | 方向键 |
新增、新建、添加的快捷键 | Alt +Insert |
窗口变大小 | Ctrl +Shift +F12 |
切窗口 | Alt +左右箭头 |
运行 | Ctrl +Shift +F10 |
单行注释 | Ctrl +/ |
多行注释 | Ctrl +Shift +/ |
定位方法属性变量 | 停到单词下单击Ctrl |
纠正错误 | Alt +Enter |
一、面向对象
1.1 final关键字
- final修饰的变量不能被修改
- final修饰的变量必须显式初始化
- final修饰的方法不能被覆盖
- final修饰的类不能被继承
- 如果修饰的引用,那这个引用只能指向一个对象,不可重新赋值,但是被指向的对象可以修改
- 构造方法不能被final修饰
1.1.1 final修饰的(基本数据类型)变量不能被修改
实例:
public class Test {
public static void main(String[] args) {
final int k = 10;
k = 20;
}
}
运行结果:
java: 无法为最终变量k分配值
1.1.2 final修饰的实例变量
- 被final修饰的实例变量必须手动赋值,不会自动赋默认值
- 在构造方法里赋值也行,setter方法不可行
实例:
public class Test {
public static void main(String[] args) {
}
}
class Person{
final int age = 0;//手动赋值
final boolean sex;//被final修饰的实例变量必须手动赋值,系统不会自动赋默认值
public Person(){
this.sex = true;//手动赋值
}
public Person(boolean b){
this.sex = b;//手动赋值
}
}
- 实例变量被final关键字修饰后,一般会加static变为静态
- static final修饰的变量称为常量,常量不可更改,一般为公开的
实例:
public class Test {
public static void main(String[] args) {
}
}
class Chinese{
String idCard;
String name;
static final String COUNTRY = "中国";
}
1.1.3 final修饰的引用变量不能被修改
- final修饰的引用变量不能被修改,但引用内部的数据是可以被修改的
实例:
public class Test {
public static void main(String[] args) {
final Person p = new Person(30);//该引用只能指向一个对象,并且永远只指向这个对象
p = new Person(30);//p引用保存的地址是不可修改的
System.out.println(p.age);
p.age = 40;//引用内部的数据是可以被修改的
System.out.println(p.age);
}
}
class Person{
int age;
public Person() {
}
public Person(int age) {
this.age = age;
}
}
运行结果:
java: 无法为最终变量p分配值
1.1.4 final关键字修饰的类不能被继承
实例:
public class Test {
public static void main(String[] args) {
}
}
final class A{
}
class B extends A{
}
运行结果:
java: 无法从最终A进行继承
1.1.5 final修饰的方法不能被覆盖重写
实例:
public class Test {
public static void main(String[] args) {
}
}
class A{
public final void doSome(){
}
}
class B extends A{
public void doSome(){
}
}
运行结果:
java: B中的doSome()无法覆盖A中的doSome()
被覆盖的方法为final
1.2 抽象类
抽象类是类和类之间有共同特征再抽象成抽象类
- 在Java中采用abstract定义的类就是抽象类,采用abstract定义的方法就叫抽象方法
- 抽象类属于引用数据类型
- 抽象方法只能存在于抽象类中
- 抽象父类中有抽象方法,继承的子类要么也为抽象类,要么在子类中覆盖抽象方法
- 抽象类无法实例化,无法创建对象
- 抽象类也有构造方法,供子类使用
- 抽象类不能用final修饰,抽象类就是用来被继承的
- 抽象方法不能被final修饰,抽象方法就是用来被子类实现的
- 向上转型多态编译时,会绑定父类中的方法,在执行时会执行子类的。
1.2.1 采用abstract定义抽象类
抽象类定义的语法格式:
[修饰符列表] abstract class 类名{
类体;
}
1.2.2 抽象方法
抽象方法是没有实现、没有方法体的方法
例如:
public abstract void doSome();
实例:
public class Test {
public static void main(String[] args) {
A a = new A();//报错,抽象类无法实例化
B b = new B();//非抽象子类可以实例化
}
}
abstract class A{
public abstract void doSome();
public void doOther(){//抽象类中可以存在非抽象方法
}
}
class B extends A{
public void doSome(){//抽象方法在非抽象子类中必须重写
}
}
abstract class C extends A{//在抽象子类中不需要重写父类抽象方法
}
1.3 接口
1.3.1 接口的基础语法
- 接口也是一种引用数据类型,接口编译后也是.class字节码文件
- 接口是完全抽象的(抽象类是半抽象)
- 接口是可以继承的,并且支持多继承
- 接口中只能包含常量和抽象方法(没有构造方法)
- 接口中所有元素都是公开的,public可以省略
- 接口中抽象方法默认都是public abstract,可以省略
- 接口中变量都是public static final,可以省略,必须显式初始化
- 一个非抽象的类,实现接口时,必须将接口中所有方法实现
- 一个类可以实现多个接口
- extends和implements同时出现时,必须extends写在前,implements写在后
- 使用接口时,可以使用多态(父类型引用指向子类型对象)
定义接口的语法格式:
[修饰符列表] interface 接口名{
}
实例:
public class Test {
public static void main(String[] args) {
}
}
interface A{
}
interface B extends A{
}
interface C extends A,B{
}
interface MyMath{
//public static final double PI=3.12;
double PI=3.12;
//public abstract int sum(int a,int b);
int sum(int a,int b);
}
- 接口可以看作是类,类之间叫继承,类和接口之间叫实现通过implements关键字完成
- 当一个非抽象的类实现接口时,必须将接口中的所有抽象方法全部实现(覆盖重写)
- 子类的方法访问权限要更低(封闭)才行
实例:
public class Test {
public static void main(String[] args) {
}
}
interface MyMath{
double PI=3.12;
int sum(int a,int b);
int sub(int a,int b);
}
class MyMathImp implements MyMath{//重写的方法访问权限必须更封闭
public int sum(int a,int b){
return a+b;
}
public int sub(int a,int b){
return a-b;
}
}
//abstract class MyMathImp implements MyMath{
//}
接口和多态联合使用
实例:
public class Test {
public static void main(String[] args) {
MyMath mm = new MyMathImp();
System.out.println(mm.sum(30,10));
System.out.println(mm.sub(30,10));
}
}
interface MyMath{
double PI=3.14;
int sum(int a,int b);
int sub(int a,int b);
}
//class MyMathImp implements MyMath{
//
//}
class MyMathImp implements MyMath{
public int sum(int a,int b){
return a+b;
}
public int sub(int a,int b){
return a-b;
}
}
运行结果:
40
20
-
一个类可以实现多个接口,弥补了类不能多继承的缺点
-
接口之间在强制类型转换时,没有继承关系也可以强转。但是运行时可能会出现ClassCastException异常(假装当没讲,容易混淆没啥用)
实例:
public class Test {
public static void main(String[] args) {
A a = new D();
B b = new D();
C c = new D();
B b2 = (B)a;//A和B之间没有继承关系
b2.b();
}
}
interface A{
void a();
}
interface B{
void b();
}
interface C{
void c();
}
class D implements A,B,C{
public void a(){
}
public void b(){
System.out.println("b....");
}
public void c(){
}
}
运行结果:
b....
实例:
public class Test {
public static void main(String[] args) {
B b = new C();
if(B instanceof A){
A a = (A)b;//如果没有检查,就会报错
/*Exception in thread "main" java.lang.ClassCastException: class C cannot be cast to class A (C and A are in unnamed module of loader 'app')
at Test1.main(Test1.java:4)*/
}
}
}
interface A{
}
interface B{
}
class C implements B{
}
- extends和implements同时出现,extends写在前,implements写在后
- 接口通常提取的是行为动作
实例:
public class Test {
public static void main(String[] args) {
Flyable f = new Cat();
f.fly();
}
}
class Animal{
}
interface Flyable{
void fly();
}
class Cat extends Animal implements Flyable{
public void fly(){
System.out.println("飞猫起飞!");
}
}
运行结果:
飞猫起飞!
1.3.2 接口在开发中的作用
接口在开发中的作用,类似多态在开发中的作用
实例:
public interface FoodMenu {
void xiHongShijiDan();
void yuXiangRouSi();
}
public class Customer {
//凡是可以用has a形容的,统一用属性的方式存在
//顾客有一个菜单
private FoodMenu foodMenu;
public Customer() {
}
public Customer(FoodMenu foodMenu) {
this.foodMenu = foodMenu;
}
public FoodMenu getFoodMenu() {
return foodMenu;
}
public void setFoodMenu(FoodMenu foodMenu) {
this.foodMenu = foodMenu;
}
public void order(){
//FoodMenu fm = this.getFoodMenu();
foodMenu.xiHongShijiDan();
foodMenu.yuXiangRouSi();
}
}
public class ChinaCooker implements FoodMenu{
public void xiHongShijiDan() {
System.out.println("西红柿鸡蛋中国味道");
}
public void yuXiangRouSi() {
System.out.println("鱼香肉丝中国味道");
}
}
public class AmericaCooker implements FoodMenu{
public void xiHongShijiDan() {
System.out.println("西红柿鸡蛋美国味道");
}
public void yuXiangRouSi() {
System.out.println("西红柿鸡蛋美国味道");
}
}
public class Restaurant {
public static void main(String[] args) {
FoodMenu cooker1 = new ChinaCooker();
Customer c1 = new Customer(cooker1);
c1.order();
}
}
运行结果:
西红柿鸡蛋中国味道
鱼香肉丝中国味道
1.3.3 类和类之间的关系
is a、has a、 like a
- is a:Cat is a Animal 表示继承关系
凡是满足is a的表示继承关系 - has a:I has a Pen表示关联关系
关联关系通常以属性形式存在 - like a:Cooker like a FoodMenu表示实现关系
实现关系通常是类实现接口
1.3.4 抽象类和接口的区别
- 抽象类是半抽象的,接口是完全抽象的
- 抽象类有构造方法,接口没有构造方法
- 接口之间支持多继承,类之间只有单继承
- 一个类可以实现多接口,一个抽象类只能继承一个类
- 接口中只能出现常量和抽象方法
- 一般接口使用的比较多,接口一般是对行为的抽象
1.4 包机制
- package是Java包机制。是为了方便程序的管理,不同功能的类分别存放在不同的包下
- package是一个关键字,后面加包名。例如:package com.baidu.javase.chapter17
- package只允许出现在Java源代码的第一行(注释行不算)
- 包名一般采用公司域名倒序的方式
- 包名命名规范:公司域名倒序+项目名+模块名+功能名
package的Java程序编译和运行:
//在chapter17目录下的HelloWorld.java文件
package com.baidu.javase.chapter17;
//此时类名已变为com.baidu.javase.chapter17.HelloWorld
public static void main(String[] args){
System.out.println("Hello World!");
}
在CMD中编译:
javac -d . HelloWorld.java
javac负责编译
-d带包编译
. 代表编译之后生成的东西放在当前目录下
HelloWorld.java 被编译的java文件名
在CMD中运行:
java com.baidu.javase.chapter17.HelloWorld
1.4.1 使用包机制
- 如果两个程序在同一package下,包名可以省略。不在同一包名下,必须写全包名
实例:
package com.baidu.javase.chapter17;
public class Test1{
public static void main(String[] args){
com.baidu.javase.chapter17.HelloWorld hw = new com.baidu.javase.chapter17.HelloWorld();
HelloWorld hw2 = new HelloWorld();//同一包名下,可以省略包名
}
}
package com;
public class Test2{
public static void main(String[] args){
//Test1在com.baidu.javase.chapter17包下,Test2在com包下,不能省略包名
com.baidu.javase.chapter17.HelloWorld hw = new com.baidu.javase.chapter17.HelloWorld();
}
}
1.4.2 使用import机制
- import语句只能出现在package语句之下,class声明语句之上
- 当两个类不在同一包时,使用import。在同一包内,不需要使用
- lang包下的东西都不需要手动导,程序自动导
实例:
package com;
import com.baidu.javase.chapter17.HelloWorld;//将需要的类导入
public class Test3{
public static void mian(String[] args){
HelloWorld hw = new HelloWorld();
}
}
package com.baidu.javase.chapter17;
public class Test4{
public static void mian(String[] args){
java.util.Scanner s = new java.util.Scanner(System.in);//必须写明包
}
}
package com.baidu.javase.chapter17;
import java.util.Scanner;
public class Test5{
public static void mian(String[] args){
//此类和Scanner类不在同一个包
java.util.Scanner s = new java.util.Scanner(System.in);//必须写明包
}
}
在Test5中也可以写为:
import java.util.*;//*在此只能代表某些类的名字
此种写法运行速度不会比以上写法慢
1.5访问权限控制符
- private 私有
- protected 受保护
- public 公开
1.5.1 访问控制修饰符的控制范围
- private 只能在本类中访问
- protected 只能在本类,同包,子类中访问
- public 在任何位置都能访问
- 默认 只能在本类和同包下访问
范围由大到小:public > protected > 默认 > private
1.5.2 访问控制修饰符修饰的对象
- 属性(4个都能用)
- 方法(4个都能用)
- 类(public和默认可以)
- 接口(public和默认可以)
1.6 Object类
Object类中的常用方法可以查阅Java类库的帮助文档
- API是应用程序编程接口
- 整个JDK类库就是一个Java SE的API
- 每一个API都会配置一个API帮助文档
目前需要掌握的Object的方法:
protected Object clone()//负责对象克隆
int hashCode()//获取对象哈希值的一个方法
boolean equals(Object obj)//判断两个对象是否相等
String toString()//将对象转换成字符串形式
protected void finalize()//垃圾回收器负责调用的方法
1.6.1 Object类的toString方法
- toString方法可以将一个Java对象转换成字符串表示形式
实例:
public class test {
public static void main(String[] args) {
MyTime t1 = new MyTime(1970,1,1);
String s1 = t1.toString();
System.out.println(s1);
}
}
class MyTime{
int year;
int month;
int day;
public MyTime(){
}
public MyTime(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
}
运行结果:
MyTime@49e4cb85
但是我们并不期待这样的结果,我们期待的是具体的日期结果,所以最好重写toString()方法。
- 建议所有子类都重写toString()方法
实例:
public class test {
public static void main(String[] args) {
MyTime t1 = new MyTime(1970,1,1);
String s1 = t1.toString();
System.out.println(s1);
}
}
class MyTime{
int year;
int month;
int day;
public MyTime(){
}
public MyTime(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
public String toString(){
return this.year + "年" + this.month + "月" + this.day + "日";
}
}
运行结果:
1970年1月1日
1.6.2 Object类的equals方法
- equals方法用来判断两个对象是否相等
- “==”用来判断两个基本数据类型是否相等,equals用来判断两个引用数据类型是否相等
- String类重写了equals方法
- String类重写了toString方法
equals方法的源代码:
public boolean equals(Object obj){
return (this == obj);//源代码中用的“==”
}
实例:
public class test {
public static void main(String[] args) {
MyTime t1 = new MyTime(1970,1,1);
MyTime t2 = new MyTime(1970,1,1);
//判断两个Java对象不能使用“==”
System.out.println(t1==t2);//此处判断的是t1、t2中保存的对象内存地址是否相等
}
}
class MyTime{
int year;
int month;
int day;
public MyTime(){
}
public MyTime(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
}
运行结果:
false
重写equals方法
实例:
public class test {
public static void main(String[] args) {
MyTime t1 = new MyTime(1970,1,1);
MyTime t2 = new MyTime(1970,1,1);
System.out.println(t1==t2);
System.out.println(t1.equals(t2));
}
}
class MyTime{
int year;
int month;
int day;
public MyTime(){
}
public MyTime(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
public boolean equals(Object obj) {
if(ibj == null){
return false;
}
//当年月日都相等时,认为两个对象相等
int year1 = this.year;
int month1 = this.month;
int day1 = this.day;
//想要访问子类中特有的东西
if(obj instance MyTime){
return false;
}
//向下转型 强转
MyTime mt = (MyTime) obj;
int year2 = mt.year;
int month2 = mt.month;
int day2 = mt.day;
if(this.year == year2&&this.month == month2&&this.day == day2){
return true;
}
return false;
}
}
运行结果:
false
true
比较两个字符串必须用equals方法
实例:
public class test {
public static void main(String[] args) {
String s1 = "Hello";
String s2 = "Hello";
String s3 = new String("Hello");
String s4 = new String("Hello");
System.out.println(s1==s2);
System.out.println(s1.equals(s2));
System.out.println(s3==s4);
System.out.println(s3.equals(s4));
}
}
运行结果:
true
true
false
true
String类重写了toString()方法
实例:
public class test {
public static void main(String[] args) {
String x = new String("Hello");
System.out.println(x);//引用数据类型当不写toString方法时,自动调用toString方法
System.out.println(x.toString());//如果没重写toString,结果应为String@十六进制的地址
}
}
运行结果:
Hello
Hello
实例:
public class test {
public static void main(String[] args) {
Student s1 = new Student(111,"北京");
Student s2 = new Student(112,"北京");
System.out.println(s1 == s2);//false
System.out.println(s1.equals(s2));//true
// Student s3 = new Student(111,new String("北京"));
// Student s4 = new Student(112,new String("北京"));//两个北京的内存地址不同,如果用equals比较那两者通用
// System.out.println(s3 == s4);//false
// System.out.println(s3.equals(s4));//false
}
}
class Student{
int no;
String school;
public Student() {
}
public Student(int no, String school) {
this.no = no;
this.school = school;
}
public String toString() {
return "学号"+no+"学校名称"+school;
}
public boolean equals(Object obj) {
if(obj == null||!(obj instanceof Student)) return false;
if(this == obj) return true;
Student s = (Student) obj;
return this.no == s.no && this.school.equals(s.school);
//return this.no == s.no && this.school == s.school);//不可以
}
}
运行结果:
false
false
equals方法的深层次理解
重写equals方法要彻底
实例:
public class test {
public static void main(String[] args) {
Address addr1 = new Address("北京","海淀区","111");
User u1 = new User("张三",addr1);
// User u1 = new User("张三", new Address("北京","海淀区","111"));
User u2 = new User("张三", new Address("北京","海淀区","111"));
System.out.println(u1.equals(u2));//true
}
}
class User{
String name;
Address addr;
public User() {
}
public User(String name, Address addr) {
this.name = name;
this.addr = addr;
}
//重写equals方法
//当一个用户的姓名和地址都相同时,表示是同一个用户。这里判断两个User对象是否相等
public boolean equals(Object obj) {
if(obj == null||!(obj instanceof User)) return false;
if(this == obj) return true;
User u = (User)obj;
if(this.name.equals(u.name) && this.addr.equals(u.addr)){
return true;
}
return false;
}
}
class Address{
String city;
String street;
String zipcode;
public Address() {
}
public Address(String city, String street, String zipcode) {
this.city = city;
this.street = street;
this.zipcode = zipcode;
}
//这里不重写equals方法会导致上面的条件里addr的equals比较的是内存地址
public boolean equals(Object obj) {
if(obj == null||!(obj instanceof Address)) return false;
if(this == obj) return true;
Address a = (Address)obj;
if(this.city.equals(a.city) && this.street.equals(a.street) && this.zipcode.equals(a.zipcode)){
return true;
}
return false;
}
}
运行结果:
true
1.6.3 Object类的finalize方法(了解即可,已过时)
- JDK9之后已删除此方法
在Object类中的源代码:
protected void finalize() throws Throwable{}
只有一个方法体
- 当一个对象即将被垃圾回收器回收时,会自动调用finalize()
- finalize是一个垃圾销毁时机,如果希望在对象即将被销毁时执行一段代码,这段代码写在finalize方法中
实例:
public class test {
public static void main(String[] args) {
for (int i=0;i<1000000;i++){
Person p = new Person();
//将p变成垃圾
p = null;
}
}
}
class Person{
//重写finalize方法
//Person类的对象被垃圾回收器回收时,垃圾回收器负责调用p.finalize();
protected void finalize() throws Throwable {
System.out.println("即将被销毁");
}
}
- 垃圾回收器不是轻易启动的,垃圾太少或者时机未到等条件下可能启动可能不启动
system.gc();//建议gc启动,可能听,也可能不听,仅仅是概率大一些
1.6.4 Object类的hashCode方法
hashCode的源代码:
public native int hashCode();
- hashCode()方法带有native关键字,底层调用了C++程序
- hashCode()方法返回的是哈希码:实际上就是一个内存地址,经过哈希算法得到一个值
实例:
public class test {
public static void main(String[] args) {
Object o = new Object();
int hashCode = o.hashCode();
System.out.println(hashCode);
}
}
运行结果:
2083562754
1.7 匿名内部类
- 内部类:在类的内部又定义了一个新的类
- 内部类分为:静态内部类、实例内部类、局部内部类
实例:
class Test{
static class Inner1{//静态内部类
}
class Inner2{//实例内部类
}
public void doSome(){
int i = 100;//局部变量
class Inner3{//局部内部类
}
}
public void doOther(){
//doSome()中的局部内部类Inner3,在doOther()中不能用
}
}
- 匿名内部类是局部内部类的一种,没有类名
未使用匿名内部类
实例:
public class Test {
public static void main(String[] args) {
MyMath mm = new MyMath();
mm.MySum(new ComputerImp(),100,200);
}
}
interface Computer{
//抽象方法
int sum(int a,int b);
}
class ComputerImp implements Computer{//接口的实现类
public int sum(int a, int b) {
return a + b;
}
}
class MyMath{
public void MySum(Computer c,int a,int b){
int retValue = c.sum(a,b);
System.out.println(a+"+"+b+"="+retValue);
}
}
使用匿名内部类,可以不写接口的实现类
实例:
public class Test {
public static void main(String[] args) {
MyMath mm = new MyMath();
mm.MySum(new Computer(){
public int sum(int a,int b){
return a + b;
}
},100,200);
}
}
interface Computer{
//抽象方法
int sum(int a,int b);
}
class MyMath{
public void MySum(Computer c,int a,int b){
int retValue = c.sum(a,b);
System.out.println(a+"+"+b+"="+retValue);
}
}
}
public void doOther(){
//doSome()中的局部内部类Inner3,在doOther()中不能用
}
}
- 匿名内部类是局部内部类的一种,没有类名
未使用匿名内部类
实例:
```java
public class Test {
public static void main(String[] args) {
MyMath mm = new MyMath();
mm.MySum(new ComputerImp(),100,200);
}
}
interface Computer{
//抽象方法
int sum(int a,int b);
}
class ComputerImp implements Computer{//接口的实现类
public int sum(int a, int b) {
return a + b;
}
}
class MyMath{
public void MySum(Computer c,int a,int b){
int retValue = c.sum(a,b);
System.out.println(a+"+"+b+"="+retValue);
}
}
使用匿名内部类,可以不写接口的实现类
实例:
public class Test {
public static void main(String[] args) {
MyMath mm = new MyMath();
mm.MySum(new Computer(){
public int sum(int a,int b){
return a + b;
}
},100,200);
}
}
interface Computer{
//抽象方法
int sum(int a,int b);
}
class MyMath{
public void MySum(Computer c,int a,int b){
int retValue = c.sum(a,b);
System.out.println(a+"+"+b+"="+retValue);
}
}