1.搭建环境
1)新建空工程
2)新建module
小技巧
快速生成main方法:psvm
快速生成输出语句:sout
final关键字
1.final时Java语言中的一个关键字
2.final表示最终的、不可变的。
3.final可以修饰变量以及方法,还有类等。
final修饰的变量一般会加static变为静态的,存在方法区
static final 联合修饰的变量成为“常量”,常量名建议大写,单词之间用‘_’衔接
常量和静态变量都是存储在方法去,并且都是在类加载时初始化。
final修饰的变量一旦赋值,就无法再分配值。
(final修饰的变量,只能赋一次值)
public static void main(String[] args) {
int i = 100;
//可以为局部变量i重新赋值
i=200;
final int k=100;
//无法为局部变量k重新赋值
k=200;
final int m;
m=200;
//一旦赋值,无法再分配值
m=300;
}
new一个对象,就会产生新的内存地址。使用final修饰该引用变量后,就不能再重新new一个对象,与后面的传值无关。并且在该方法执行过程中,该引用指向对象后,该对象不会被垃圾回收器回收。直到当前方法结束,才会释放空间。
(虽然final的引用只能指向1个对象,并且它永远都只能指向该对象,但是对象内部的数据可以被修改)
public static void main(String[] args) {
//可行
Person p = new Person(20);
//报错
final Person p1= new Person(30);
p1 = new Person(30);//不能指向其他地址
p1.age=40;//可以重新赋值
}
static class Person{
int age;
public Person() {
}
public Person(int age) {
this.age = age;
}
}
final修饰的实例变量,系统不负责赋默认值,要求必须手动赋值。手动赋值,在定义变量时赋值可以,在构造方法中赋值也可以。
//用final修饰的实例变量必须手动赋值
static class User{
final int age;
}
/*因为系统会调用无参构造默认赋0,
但是final修饰的方法必须手动赋值,
因此只要在系统自动赋值前,
手动赋值便不会报错*/
static class User{
final int age;
public User() {
this.age = 80;
}
}
final修饰的方法
被final修饰的方法无法被覆盖、被重写。
//可行
class C{
public void doSome(){
System.out.println("C.doSome....");
}
}
class D extends C{
public void doSome(){
System.out.println("D.doSome....");
}
}
//报错
class C{
public final void doSome(){
System.out.println("C.doSome....");
}
}
class D extends C{
public void doSome(){
System.out.println("D.doSome....");
}
}
final修饰的类
B类继承A类,相当于对 A类的功能进行扩展。如果不希望别人对A类型进行扩展。可以给A类加final关键字,这样的话A类就无法被继承了。
//可行
class A{
}
class B extends A{
}
//报错
final class A{
}
class B extends A{
}
public static void main(String[] args) {
//多态
C c= new D();
//报错!编译器认为c是c类,c类没有doTher方法
c.doTher();
//可行!调用子类特有的方法时,需要向下转型。
if(c instanceof D){//如果c确实指向D类型
D d1=(D)c;
d1.doTher();
}
}
class C{
public void doSome(){}
}
class D extends C{
public void doTher(){}
}
抽象类
1.什么是抽象类?
抽象类:将类与类之间的共同特征进一步抽象,形成抽象类。由于类本身是不存在的,所以抽象类无法创建对象(无法实例化)。抽象类是用来被子类继承的。
public class AbstractTest {
public static void main(String[] args) {
new Account();
}
abstract static class Account{}
}
final和abstract不能联合使用
public class AbstractTest {
public static void main(String[] args) {
}
final abstract static class Account{}
}
子类继承抽象类,抽象类的子类可以是抽象类
public class AbstractTest {
public static void main(String[] args) {
}
abstract static class Account{}
class CreaditAccount extends Account{}
abstract class BankAccount extends Account{}
}
抽象类无法实例化,但存在构造方法,供子类使用。(子类的对象在创建的时候,通常会先调用父类的无参构造方法,供子类创建对象。)
父类定义了有参构造而子类又没用显示调用,系统默认子类调用父类无参构造时会报错。(当子类new对象的时候,super()如果找不到父类无参构造就会报错。)
public class AbstractTest {
public static void main(String[] args) {
}
abstract static class Account{
public Account(String s) {
}
}
class CreaditAccount extends Account{}
}
2.抽象类属于什么类型?
抽象类也属于引用数据类型
3.抽象类怎么定义?
[修饰符列表] abstract class 类名{
类体;
}
抽象方法:表示没有实现的方法,没有方法体的方法。
例如:public abstract void doSome();
抽象方法特点是:
特点1:方法体,以分号结尾。
特点2:前面修饰符列表中有abstract关键字。
抽象类不一定有抽象方法,但抽象方法必须出现在抽象类,可以有非抽象方法。
public class AbstractTest {
public static void main(String[] args) {
}
static class Account{
public abstract void withdraw();
}
}
抽象类有抽象方法时,非抽象的子类继承时需要重写/覆盖从父类继承过来的抽象方法
Java语言中没有方法体的方法不一定是抽象方法。
object类中有很多方法没有方法体,都是以“;”结尾。但是他们都不是抽象方法,例如:
这个方法底层调用了C++写的动态链接库程序。(native关键字表示调用JVM本地程序)
接口
1.接口的基础语法
①接口也是一种引用数据类型
②接口是完全抽象的。(抽象类是半抽象的)👇
interface MyMath{
void doSome(){};
}
③接口的定义👇
[修饰符列表] interface 接口名{}
public class Test01 {
public static void main(String[] args) {
}
interface A{
}
}
接口与接口之间支持多继承👇
public class Test01 {
public static void main(String[] args) {}
interface A{}
interface B{}
interface C extends A,B{}
}
④接口中只包含两部分内容,一部分是:常量;一部分是抽象方法。(接口中没有其他内容)
⑤接口中所有元素都是public修饰的。
⑥接口中的抽象方法定义时,public abstract修饰符可以省略。👇
interface MyMath{
int sum(int a,int b);
}
⑦接口中的常量定义时,public static final修饰符可以省略。👇
interface MyMath{
double PI=3.1415926535;
}
⑧类继承类为extend,类实现接口为implement👇
interface MyMath{}
class MyMathImpl implements MyMath{}
⑨实现类必须重写/覆盖/实现接口的方法(因为接口是抽象类,非抽象类不能有抽象方法)👇
interface MyMath{
int sum(int a, int b);
int sub(int a ,int b);
}
class MyMathImpl implements MyMath{}
实现类的访问权限只能比接口更高👇
interface MyMath{
int sum(int a, int b);
int sub(int a ,int b);
}
class MyMathImpl implements MyMath{
int sum(int a,int b){
return a+b;
}
int sub(int a,int b){
return a-b;
}
}
interface MyMath{
int sum(int a, int b);
int sub(int a ,int b);
}
class MyMathImpl implements MyMath{
public int sum(int a,int b){
return a+b;
}
public int sub(int a,int b){
return a-b;
}
}
可以使用多态(父类型的引用指向子类型的对象)👇
public class Test01 {
public static void main(String[] args) {
MyMath mm= new MyMathImpl();
//调用接口的方法(面向接口编程)
int result1 = mm.sub(10,20);
System.out.println("sub="+result1);
int result2 = mm.sum(10,20);
System.out.println("sum="+result2);
}
interface MyMath{
int sum(int a, int b);
int sub(int a ,int b);
}
static class MyMathImpl implements MyMath{
public int sum(int a,int b){
return a+b;
}
public int sub(int a,int b){
return a-b;
}
}
}
重点:一个类可以实现多个接口,而Java中类和类之间支持单继承。现实中存在多继承,接口弥补了单继承带来的缺陷。(必须把接口的所有方法都实现了)👇
interface A{
void m1();
}
interface B{
void m2();
}
interface C{
void m3();
}
class D implements A,B,C{
public void m1(){}
public void m2(){}
public void m3(){}
}
接口和接口之间在进行强制类型转换的时候,没有继承关系也可以强转,但是编译没问题,运行时可能出现CLassCastException异常。向下转型养成if+instanceof判断的好习惯。
public static void main(String[] args) {
A a=new D();
B b=new D();
C c=new D();
//有继承关系可以向下或向上转型
B b2=(B)a;
//无继承关系,编译没问题,运行有问题
M m=new E();
if(m instanceof K){
K k=(K)m;
}
}
继承和实现都存在时
语法:extends关键字在前,implements关键字在后。
class Animal{}
interface Flyable{}
class cat extends Animal implements Flyable{}
2.接口在开发中的作用
面向接口编程,可以降低程序的耦合度,提高程序的扩展力,符合OCP开闭原则。
接口+多态才能达到降低耦合度
接口可以将调用者和实现者解耦合:
调用者面向接口调用,
实现者面向接口编写实现。
👇测试
public static void main(String[] args) { //创建厨师对象 FoodMenu cooder1= new AmericCooker(); FoodMenu cooder2= new ChinaCooker(); //创建顾客对象,并传入厨师对象 Customer customer= new Customer(cooder1); //顾客点餐 customer.order(); }
👇接口:菜单类
public interface FoodMenu{ //西红柿炒蛋 void shiZiChaoJiDan(); //鱼香肉丝 void yuXiangRouSi(); }
👇顾客,手里有一个菜单
public static class Customer{ private FoodMenu foodMenu; //提供一个点菜的方法 public void order(){ //先拿到菜单才能点菜,调用get方法拿菜单 FoodMenu fm=this.getFoodMenu(); fm.shiZiChaoJiDan(); fm.yuXiangRouSi(); //也可以不调用get方法,因为子啊奔雷中私有的属性是可以访问 /*foodMenu.shiZiChaoJiDan(); foodMenu.yuXiangRouSi();*/ } public Customer() { } public Customer(FoodMenu foodMenu) { this.foodMenu = foodMenu; } public FoodMenu getFoodMenu() { return foodMenu; } public void setFoodMenu(FoodMenu foodMenu) { this.foodMenu = foodMenu; } }
👇中餐厨师,实现菜单上的菜
public static class ChinaCooker implements FoodMenu{ @Override public void shiZiChaoJiDan() { System.out.println("中餐厨师做的西红柿炒鸡蛋!"); } @Override public void yuXiangRouSi() { System.out.println("中餐厨师做的鱼香肉丝!"); }
👇西餐厨师,实现菜单上的菜
public static class AmericCooker implements FoodMenu{ @Override public void shiZiChaoJiDan() { System.out.println("西餐厨师做的西红柿炒鸡蛋!"); } @Override public void yuXiangRouSi() { System.out.println("西餐厨师做的鱼香肉丝!"); }
is a、has a、like a
is a:Cat is a Animal(猫是一个动物)
凡是能满足is a的表示“继承关系”
has a:I has a Pen(我有一支笔)
凡是能满足has a的表示“关联关系”(关联关系通常以“属性”的形式存在。)
like a:Cooker like a FoodMenu(厨师像一个菜单一样)
凡是能满足like a关系的表示“实现关系”(实现关系通常是“类实现接口。”)
接口类与接口的区别
抽象类是半抽象
接口是完全抽象的
抽象类中有构造方法
接口中没有构造方法
接口和接口之间支持多继承
类和类之间只能单继承
一个类可以同时实现多个接口
一个抽象类只能继承一个类(单继承)
接口中只允许出现常量和抽象方法
package和import
1.为什么要用package?
package是Java中包机制。包机制的作用是为了方便程序的管理,不同的功能的类分别存放在不同的包下。
2.package包怎么用?
package是一个关键字,后面加包名,例如:
package com.bjpowernode.javase.chapter17;
注意:package语句只允许出现在Java源代码的第一行
3.包名命名规范
一般都采用公司域名倒叙的方式(因为公司域名具有全球唯一性)
:公司域名倒叙+项目名+模块名+功能名
对于带有package的Java程序用cmd怎么编译、运行?
类名:com.bjpowernode.javase.chapter17.HelloWorld
(不再是HelloWorld了)
编译:
javac -d . HelloWorld.java
javac:负责编译的命令
-d:带包编译
.:代表编译之后生成的东西放到当前目录下(.代表当前目录)
HelloWorld.java:被编译的java文件名
运行:
java com.bjpowernode.javase.chapter17.HelloWorld
4.import的使用
java.lang不需要
同一个包下,不需要import。
不在同一个包下,需要import。
5.import的使用位置
import语句只能出现在package语句之下,class声明之上。
访问权限控制
1.private(私有的)
:只能在本类中访问
2.protected(受保护的)
:只能在本类、同包或子类中访问
3.public(公开的)
:在任何位置都可以访问
4.default(默认的)
:只能在本类以及同包下访问
范围大小:public>protected>默认的>private
Object类
API:应用程序编程接口
整个JDK的类库就是一个javase的API
每个API都会配置一套API帮助文档
1.toString()
:将对象转换成字符串形式
/*
源代码上toString()的默认实现:
类名@对象的内存地址转换为十六进制的形式
*/
public String toString(){
return this.getClass().getName() + '@' + Integer.toHexString(hashCode())
}
toString()设计目的:可以通过该方法将一个”java对象“转换成”字符串“的形式
没有重写toString()方法前,程序👇
public class Test01 { public static void main(String[] args) { MyTime t1=new MyTime(1970,1,1); String s1=t1.toString(); System.out.println(s1); System.out.println(t1);//输出引用时,会自动调用该引用的toString() } static 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; } } }
结果👇
Test01$MyTime@1b6d3586Test01$MyTime@1b6d3586
重写toString()方法后,程序👇
public class Test01 { public static void main(String[] args) { MyTime t1=new MyTime(1970,1,1); String s1=t1.toString(); System.out.println(s1); System.out.println(t1);//输出引用时,会自动调用该引用的toString() } static 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; } @Override public String toString() { return year +"年" + month +"月" + day+"日"; } } }
结果👇
1970年1月1日
1970年1月1日
2.finalize
:垃圾回收器负责调用的方法(只需重写,不需要程序员手动调)
/*
源代码上finalize()的默认实现:
当一个Java镀锡即将被垃圾回收器回收的时候,垃圾回收器负责调用 finalize()。
*/
protected void finalize() throws Throwable{}
静态代码块:
static{ ...... }
静态代码块在类加载时刻执行,并且只执行一次。这是一个SUN准备的类加载时机。
finalize()方法同样也是SUN为程序员准备的时机。(垃圾回收时机)
System.gc();
:建议启动垃圾回收器(因为只有垃圾到达一定数量才会启动)public static void main(String[] args) { for(int i=0;i<1000;i++) { Person p = new Person(); p = null; System.gc();//建议启动垃圾回收器 } } static class Person{ protected void finalize() throws Throwable{ System.out.println(this+"即将被回收!"); } }
3.==与equals()
:判断两个对象是否相等
/*
源代码上equals()的默认实现:
当且仅当x和y引用相同的对象( x == y具有值true )时,该方法返回true 。
*/
public boolean equals(Object obj){
return (this == obj);
}
重写equals()后👇
public class Test02 { public static void main(String[] args) { //判断两个基本数据类型的数据是否相等使用”==“ int a=100; int b=100; System.out.print("a==b:"); System.out.println(a==b); //判断两个java对象是否相等使用”equals“ MyTime t1=new MyTime(2001,7,8); MyTime t2=new MyTime(2001,7,8); //这里的”==“判断的是:t1中保存的对象内存地址和t2中保存的对象内存地址是否相等 System.out.print("t1==t2:"); System.out.println(t1==t2); System.out.print("t1.equals(t2):"); System.out.println(t1.equals(t2)); } static 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; } //重写equals @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if(o instanceof MyTime) { MyTime myTime = (MyTime) o; return year == myTime.year && month == myTime.month && day == myTime.day; } return false; } @Override public int hashCode() { return Objects.hash(year, month, day); } } }
重写equals()前程序执行结果👇
a==b:true
t1==t2:false
t1.equals(t2):false
因为equals()默认实现是”==“比较,想要判断两个对象的内容是否相等,需要重写
重写equals()后程序执行结果👇
a==b:true
t1==t2:false
t1.equals(t2):true
注意:string类重写了equals()、toString()方法
例题:比较两个用户
public class Test03 { public static void main(String[] args) { User u1=new User("zhangsan",new Address("北京","大兴区","1111111")); User u2=new User("zhangsan",new Address("北京","大兴区","1111111")); System.out.println(u1.equals(u2)); } static class User{ //用户名 String name; //用户住址 Address addr; public User() {} public User(String name, Address addr) { this.name = name; this.addr = addr; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; return Objects.equals(name, user.name) && Objects.equals(addr, user.addr); } } static 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; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Address address = (Address) o; return Objects.equals(city, address.city) && Objects.equals(street, address.street) && Objects.equals(zipcode, address.zipcode); } } }
运行结果:true
4.clone()
:负责对象克隆
protected Object clone(){
throws CloneNotSupportedException
}
5.hashCode()
:获取对象哈希值
/*
源代码上hashCode()的默认实现:
实际上就是一个Java对象的内存地址,经过哈希算法,得出的一个值。(带有native关键字,底层调用C++)
*/
public native int hashCode()
public class Test05 { public static void main(String[] args) { Object o = new Object(); int hashCodeValue=o.hashCode(); System.out.println(hashCodeValue);//460141958 MyClass m= new MyClass(); int hashCodeValue2=m.hashCode(); System.out.println(hashCodeValue2);//1163157884 } static class MyClass{} }
内部类
:在类的内部又定义了一个新的类。(使用内部类编写的代码,可读性很差,能不用尽量不用)
1.实例内部类
:类似于实例变量
2.静态内部类
:类似于静态变量
3.局部内部类
:类似局部变量
3.1匿名内部类
使用匿名内部类前👇(还要定义接口的实现类)
public class Test01 { public static void main(String[] args) { MyMath mm=new MyMath(); mm.mySum(new ComputerImp(),100,90); System.out.println(); } interface Computer{ int sum(int a,int b); } static class ComputerImp implements Computer{ public int sum(int a,int b){ return a+b; } } static class MyMath{ public void mySum(Computer c,int x,int y){ int retValue= c.sum(x,y); System.out.println(retValue); } } }
使用匿名内部类后👇(不需要定义接口的实现类)
public class Test01 { public static void main(String[] args) { MyMath mm=new MyMath(); mm.mySum(new Computer(){ public int sum(int a,int b) { return a + b; } },200,300); System.out.println(); } interface Computer{ int sum(int a,int b); } static class MyMath{ public void mySum(Computer c,int x,int y){ int retValue= c.sum(x,y); System.out.println(retValue); } } }
注意:不建议使用匿名内部类,因为一个类没有名字,没办法重复使用。并且代码太乱,可读性差。