个人曾经学习java的一些记录,仅供参考
java学习-day04
Static关键字
static可以用来修饰属性和方法
static修饰的属性我们叫做静态变量、类变量
static修饰的方法我们叫做静态方法、类方法
static修饰的属性和方法不需要通过对象了调用,直接通过类名调用即可(推荐使用类名调用)
static修饰的属性存在方法区,他不属于任何对象,所以不在堆空间。
public class User {
static String USERNAME;
static int NUMBER = 100;
int count = 100;
static void show(){
System.out.println("User is show");
}
}
调用
public class Test {
public static void main(String[] args) {
//System.out.println(User.USERNAME);
//User.show();
User u1 = new User();
System.out.println("U1 NUMBER:" + u1.NUMBER);// 100
System.out.println("U1 COUNT:"+ u1.count);// 100
User u2 = new User();
u2.NUMBER = 90;
u2.count = 90;
System.out.println("U1 NUMBER:" + u1.NUMBER);// 90
System.out.println("U1 COUNT:" + u1.count);// 100
}
}
总结:
static修饰的变量叫做类变量,可以直接通过类调用,也可以通过对象调用,当其中一个对象对类变量进行改变后,那么其他对象来访问这个类变量的时候也会受到影响,而成员变量属性成员私有的,每个对象在堆空间都有一个独立的存储空间,对象只能修改自己的成员变量。
内存图:
创建对象JVM做了什么事情
1、加载类,同时初始化所有static修饰的属性和方法。按照顺序执行
2、初始化所有的成员属性和方法,按照顺序执行
3、通过new调用构造方法
public class Test {
int count;//成员变量
{
System.out.println("block 1");
}
{
System.out.println("block 2");
}
static {
System.out.println("static block 1:" + Test.NUM);
}
static int NUM = 10;//类变量
static {
System.out.println("static block 2:" + NUM);
}
public Test() {
System.out.println("Test is init");
}
public static void main(String[] args)throws Exception {
//System.out.println(Test.NUM);
// 加载类 : 初始化所有的static修饰的属性和方法
Class.forName("com.iweb.java05.Test");
}
}
静态方法
静态方法中只能直接调用静态属性和静态方法,静态方法中没有this
public class Test {
static int NUMBER = 100;
String str = "abc";
public static void show(){
System.out.println(NUMBER);
//System.out.println(str);
}
public static void main(String[] args) {
show();
}
}
内部类
声明在类中的类的叫做内部类,内部类我们可以分为:1、普通内部类 2、局部内部类 3、静态内部类 4、匿名内部类。 同时内部类也是一种多态!
/**
* 外部类
*/
public class Outer {
/**
* 外部类的方法
*/
public void out(){
System.out.println("Outer is out");
}
/**
* 内部类
*/
class Inner extends Father{
public void fn(){
show(); // 调用的是Father的方法
out(); //调用的Outer外部类的方法
}
}
}
由以上的例子我们可以看出,内部类也是一种多态,因为一个类只能直接继承一个父类,很多场景我们无法实现,可以通过内部类的方式来实现,内部类也是外部类的一种扩展。
内部类的特征
我们通过一个普通内部类来进行观察:
package com.iweb.java02;
/**
* 外部类
*/
public class Outer {
public static int NUMBER = 100;
private String username = "jack";
private String password = "123456";
/**
* 外部类的方法
*/
public void out(){
System.out.println("Outer is out");
}
public static void staticOut(){
System.out.println("Outer is static out");
}
/**
* 内部类
*/
class Inner{
private String username = "inner jack";
//不能拥有 static 修饰的属性和方法
// public static int NUMBER = 200;
public void fn(){
out(); //调用的Outer外部类的方法
System.out.println(username);// inner jack
System.out.println(Outer.this.username);// jack
System.out.println(password); // 123456
System.out.println(NUMBER); // 100
staticOut();
}
// public static void staticInner(){}
}
}
调用
package com.iweb.java02;
public class Test {
public static void main(String[] args) {
//如何调用内部类的属性和方法
Outer outer = new Outer();
//通过外部类的对象去创建内部类的对象
Outer.Inner oi = outer.new Inner();
oi.fn();//调用内部类的方法
}
}
总结:
1、内部类可以直接访问外部类的属性和方法(包含私有);
2、内部类不能由static修饰的属性和方法。
3、可以通过创建外部类的对象来创建内部类的对象,访问内部类的属性和方法
局部内部类
定义:声明在方法中类,叫做局部内部类
public class Outer {
public void show(){
// 局部内部类 : 顺序执行
class Inner{
public void inner(){
System.out.println("Inner is inner");
}
}
Inner inner = new Inner();
inner.inner();
}
public static void main(String[] args) {
Outer outer = new Outer();
outer.show();
}
}
总结:
1、局部内部类中不能有static修饰的属性和方法
2、局部内部类的作用域不能超出声明这个类的方法,只能在当前方法中被调用
3、只能通过调用该内部类所在的方法来调用局部内部类
4、局部内部类在方法中是顺序执行的。
静态内部类
static修饰的内部类
public class Outer {
static int NUMBER = 100;
static class Inner{
static String USERNAME = "jack";
public static void show(){
System.out.println(NUMBER);
}
//可以通过创建对象来访问run
public void run(){
System.out.println(NUMBER);
}
}
}
调用
public class Test {
public static void main(String[] args) {
//调用静态内部类的属性
System.out.println(Outer.Inner.USERNAME);
Outer.Inner.show();
//访问内部类的成员方法
Outer.Inner oi = new Outer.Inner();
oi.run();
}
}
匿名内部类
没有类的名称的内部类:可以理解为方法重写
public abstract class Outer {
public abstract void show();
}
public class Test {
public static void main(String[] args) {
//内部类
Outer outer = new Outer() {
public void show() {
System.out.println("inner is show");
}
};
// 需要定义外部类(父类,子类就是这个内部类,只是没有名字)的接口,可以理解为是方法重写
outer.show();
}
}
public class Test {
public static void main(String[] args) {
//内部类
Outer outer = new Outer() {
public void show() {
System.out.println("inner is show");
}
};
// 需要定义外部类(父类,子类就是这个内部类,只是没有名字)的接口,可以理解为是方法重写
outer.show();
}
}
包
包的创建:package com.dst.java04;
包的作用: 工程管理
包名全部小写
包的引用:import java.utils.*;
Java中常见的包:
java.lang: 这个包下的所有的类的引用无需引用,可以直接使用。String , 包装类
java .uitl: 工具包
java.sql : 数据库操作
java.net : 网络
java.io : 数据流 File
…
接口
我们可以通过内部类进行功能的扩展,可以通过继承进行功能的扩展。很多常见还是不能实现。 JDK提供了接口。
java继承是单继承,接口可以多实现,也就是一个子类可以有多个父接口。
接口也是一个class,是一个特殊的class,在编译的时候也会被编译成 .class文件
接口的特性
特征一:属性和方法
1、所有属性都是常量
2、所有方法都是抽象方法
3、接口没有构造函数,接口不能实例化,必须通过子类进行实例化
4、接口可以多实现
public interface User {
// 所有的变量都是 public final static 必须给定初始值 ,可以省略不写 编码规范必须不写
public final static int NUMBER = 100;
// 所有的方法都是 public abstract ,可以省略不写 编码规范必须不写
public abstract void show();
}
子类的实现
public class UserImp implements User {
@Override
public void show() {
System.out.println("User imp show");
}
}
测试
public class Test {
public static void main(String[] args) {
// 不能直接实例化,必须通过子类进行实例化
User user = new UserImp();
user.show();
}
}
特征二:接口和接口、抽象类、普通类直接的关系
1、接口可以继承接口
2、抽象类可以实现接口,可以重写接口中的抽象方法,也可以补充些(因为抽象类支持抽象方法)
3、普通类可以实现接口,一个类如果实现了接口必须重写所有父接口中的所有抽象方法
interface I {
void show_i();
}
interface G {
void show_g();
}
abstract class F {
}
class C extends F implements I, G {
@Override
public void show_i() {
}
@Override
public void show_g() {
}
}
总结:一个类可以继承一个类的同时实现多个接口
public class Test {
}
interface I{
void show();
}
interface G extends I{
}
class C implements G{
@Override
public void show() {
}
}
总结:接口可以接触接口,一个类实现了一个接口,必须重写所有父接口中的所有抽象方法
public class Test {
}
interface I{
void show();
void run();
}
abstract class F implements I{
@Override
public void show() {
}
}
class C extends F{
@Override
public void run() {
}
}
总结:抽象类可以接触接口,可以实现接口中的抽象方法,也可以不重写。
什么时候使用接口
一句话: 能用接口就用接口,接口是设计的结构。
一般的设计3层 : 接口 =>抽象类=>业务类
例子:通过程序访问数据库
分析:数据库可能存在数据库移植,本来使用的是Oracle 通过数据库移植使用了MySQL ,或者说一个业务可能使用到多个数据库。
数据库的操作 简单分为三部 : 连接、操作、关闭
DataBase.java
public interface DataBase {
//Oracle的连接 和 MySQL的连接: 参数不同 url username password
void connect();
void connect(String url,String username,String password);
void select();
void close();
}
AbstractDataBase.java
public abstract class AbstractDataBase implements DataBase{
private String url;
private String username;
private String password;
public void set(String url,String username,String password){
System.out.println("set parms");
this.url = url;
this.username = username;
this.password = password;
}
@Override
public void close() {
System.out.println("数据库关闭");
}
}
MySqlDataBase.java
public class MySqlDataBase extends AbstractDataBase implements DataBase {
@Override
public void connect() {
//设置的是mysql的数据
set("jdbc:mysql://localhost:3306/test","root","123456");
System.out.println("Oracle is connect");
}
@Override
public void connect(String url, String username, String password) {
set(url,username,password);
}
@Override
public void select() {
System.out.println("MySQL is select");
}
}
OracleDataBase.java
public class OracleDataBase extends AbstractDataBase implements DataBase {
@Override
public void connect() {
set("jdbc:oracle:thin:localhost:1521:test","scott","123456");
System.out.println("Oracle is connect");
}
@Override
public void connect(String url, String username, String password) {
set(url,username,password);
}
@Override
public void select() {
System.out.println("Oracle is select");
}
}
Test.java
public class Test {
public static void main(String[] args) {
DataBase db = new OracleDataBase();
db.connect();
db.select();
db.close();
}
}
接口和抽象类的区别:
1、抽象类和接口都不能直接实例化,如果要实例化, 只能通过子类进行实例化。
2、抽象类要被子类继承,接口要被类实现。
3、接口所有的方法都是抽象方法,抽象类中可以可以有抽象方法也可以有实例方法。
4、接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
5、抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,
一个实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。
6、抽象方法只能声明,不能实现,接口是设计的结果 ,抽象类是重构的结果
7、抽象类里可以没有抽象方法
8、抽象方法要被实现,所以不能是静态的,也不能是私有的。
9、接口可继承接口,并可多实现接口,但抽象类只能单根继承。
访问控制符
访问权限:4个
分别是:public 、缺省的、protected、private
public :可以修饰类、属性、方法。修饰的类、属性和方法任何位置都可以访问
protected: 可以修饰 属性和方法,不可以修饰类。修饰的属性和方法,可以在同包、不同包的子类、类中访问
缺省的:不是default,可以修饰类、属性和方法。是什么都不写(int i =100;) int前面没有修饰符 。同包中,同类中可以访问。
private:私有的,可以修饰属性和方法,不能修饰类。只有类中可以访问
final
1、可以修饰类、属性、方法
2、修饰的类是最终类。不能被继承
3、修饰的属性必须有初值,值不可变
4、修饰的方法不能被重写
异常
什么是异常
异常库
异常分类
运行时异常:RuntimeException和它的子类
检查异常:非RuntimeException和它子类统称为检查异常
如何处理异常
public class Test {
public static void main(String[] args) {
// 运行时异常
// int[] arr = {1,2,3};
// System.out.println(arr[3]);
// System.out.println(1/0);
// String str = null;
// str.equals("");
// 检查异常
File file = new File("E:\\a.txt");
try {
FileReader reader = new FileReader(file);
}catch (FileNotFoundException e){
e.printStackTrace();//异常的追踪
}
}
}
总结:
运行时异常不需要程序员显示的处理,当发生异常的时候交由JVM处理。 而检查异常,则需要程序员显示的处理。
处理方式
捕获:通过try … catch 块进行处理 ,在try块中编写可能发生异常的代码,而在catch块中打印异常追踪的接结果。捕获是一种积极的处理方式。
try … catch 可以有多个catch块,当发生异常的时候JVM会根据异常的类型决定进入哪一个catch块
public class Test {
public void printArr(int[] arr){
try {
// 有可能发生异常
System.out.println(arr[1]);
System.out.println("打印数组");
System.out.println(arr[0]/1);
System.out.println("计算结果");
}catch (ArrayIndexOutOfBoundsException e){
System.out.println("数组越界");
//e.printStackTrace(); // 打印异常跟踪的结果
//System.out.println(e.getMessage()); // 打印异常的信息
}catch (ArithmeticException e){
System.out.println("算数异常");
}finally {
// 无论异常是否发生都会执行:一般用于数据库关闭
// 在数据库操作的过程中可能会发生异常,如果不关闭数据库,那么连接可能被阻塞
System.out.println("finally");
}
}
public static void main(String[] args) {
Test t = new Test();
t.printArr(new int[]{1,2});
}
}
抛出:在可能发生异常的方法后使用throws关键字抛出异常,将异常的处理交给方法的调用者。是一种消极的处理方式
public class Test {
public void op(int num) throws ArithmeticException{
// try {
// System.out.println(1 / num);
// }catch (ArithmeticException e){
// System.out.println(e.getMessage()+"....");
// }
System.out.println(1 / num);
}
public static void main(String[] args) {
Test t = new Test();
// try {
// t.op(0);
// }catch (ArithmeticException e){
// System.out.println(e.getMessage());
// }
t.op(0);
}
}
演示检查异常
public class Test {
public void readFile(String path)throws Exception {
FileReader reader = new FileReader(path);
System.out.println("文件读取完毕:"+reader.read());
}
public static void main(String[] args) {
Test t = new Test();
try {
t.readFile("D:\\workspace\\XZ223\\day07\\src\\com\\iweb\\java20\\a.txt");
} catch (Exception e) {
e.printStackTrace();
}
}
}
总结:
1、可以通过抛出或者捕获异常的父类,这样可以省去写很多catch代码比较整洁
2、什么时候使用try … catch 什么时候使用 throws ,当异常发生后还需要有业务处理的时候那么就用try …catch,比如说:数据库连接,文件读取。如果不需要那么都可以抛出
3、捕获和抛出都是都不是解决异常的根本方法,只是告诫程序员发生了什么异常。帮助程序员进行异常的定位。
如何解决异常
1、根据异常的错误类查找API ,
2、API会提供异常发生的可能性
3、解决问题
自定义异常
模仿JDK的写法: NullPointerException
定义一个类继承:RuntimeException
/**
* 人妖re
* 当异常发生时候,调用者可能输入了 "人妖"
*/
public class MyException extends RuntimeException {
public MyException() {
}
public MyException(String message) {
super(message);
}
}
使用自定义异常
public class Test {
/**
* 不允许用户传入 "人妖"
* @param name
*/
public void printPerson(String name){
if(name.equals("人妖")){
throw new MyException();
}
System.out.println(name);
}
public static void main(String[] args) {
Test t = new Test();
t.printPerson("人妖");
}
}
基本数据类型和包装类
基本数据类型:是java中的类型,只能用来定义、声明某一个数据类型,没有操作方法
包装类:每个基本数据类型都对应着一个包装类,是一个引用数据类型。提供了操作方法
为什么要用包装类:因为基本数据类型只是类型的声明,没有对数据的操作方法。而包装类提供了对该类型的操作方法。
int
public class Test {
public static void main(String[] args) {
//如果希望将一个int类型转成String类型,而int并没有API可以进行操作
//Integer 提供了 toString 可以将int型转成String
int i = 10; // Integer
Integer in = new Integer(10);
System.out.println(in.toString()+10);
byte b = 10;
Byte bt = new Byte((byte) 10);
System.out.println(bt.toString()+20);
}
}
基本数据类型和包装类的对应关系
byte = Byte
short = Short
int = Integer
long = Long
float = Float
double = Double
boolean = Boolean
char = Character
自动装箱和自动拆箱
public class Test {
public static void main(String[] args) {
// 引用数据类型:
Integer a = 10;
Integer b = 20; //自动装箱
// JVM会帮助我们将a和b转成int型 : 自动拆箱
System.out.println(a+b);
}
}
反编译结果
强化记忆:
装箱: 由字面量到引用数据类型的时候叫做自动装箱,可以把堆理解为是那个箱子
拆箱:当数据用于计算的时候就会拆箱,要从堆中将数据取出进行运算
基本数据类型和包装类的比较
public class Test {
public static void main(String[] args) {
// 引用数据类型:
// byte a = 10;
// byte b = 10;
// Byte c = 10;
// Byte d = 10;
// System.out.println(a == b);//true
// System.out.println(a == c);//true
// System.out.println(c == d);//true
// int a = 200;
// int b = 200;
// Integer c = 200; // -128 127 会到常量池中取得数据 而超出这个范围的数据 需要重新生成对象
// Integer d = 200;
// System.out.println(a == b);//true
// System.out.println(a == c);//true
// System.out.println(c == d);//false
// 浮点型不进行数据区间的认证,直接new出来的所有不存在-128 到127之间的区别
float a = 100f;
float b = 100f;
Float c = 100f;
Float d = 100f;
System.out.println(a == b);//true
System.out.println(a == c);//true
System.out.println(c == d);//false
}
}
什么时候使用基本数据类型和引用数据类型
1、当这个数据用于比较运算的时候尽量使用基本数据类型
2、但用基本数据类型的缺省值存在意义的时候,使用引用数据类型
BigDecimal
public class Test {
public static void main(String[] args) {
long l = Long.MAX_VALUE+200;
System.out.println(l);
BigDecimal a = new BigDecimal(Long.MAX_VALUE);
BigDecimal b = new BigDecimal(200);
System.out.println(a.add(b));
}
}