Java基础学习总结笔记(持续更新)
Java基础学习总结笔记
Java基础
Java常用内存区域
- 栈内存空间:存储引用堆内存空间的地址
- 堆内存空间:保存每个对象的具体属性内容
- 全局数据区:保存static类型的属性
- 全局代码区:保存所有的方法定义
创建对象的过程
创建对象的过程:
- 遇到对象名时,进行类的加载(只加载一次)
- 创建对象,为这个对象在堆中开辟空间
- 为对象进行属性的初始化操作
JavaBean
JavaBean是Java写成的可重用组件
符合以下标准的Java类:称之为JavaBean:
- 类是公共的
- 有一个无参的公共的构造器
- 有属性,且有对应的get、set方法
修饰符
权限修饰符
修饰符 | 同一个类中 | 同一个包中子类无关类 | 不同包的子类 | 不同包的无关类 |
---|---|---|---|---|
private | √ | |||
默认 | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
状态修饰符
static关键字
- 声明属性:使之独立于对象,成为全局属性(亦称静态属性)
- 声明方法:使之成为”类方法”,可直接通过类名调用:<类名>.<方法名>()
- 静态成员方法只能访问静态的成员变量或方法
final关键字
- final声明的类不能有子类
- final声明的方法不能被子类覆写
- final修饰的变量
- 基本类型:修饰数据值,不能被改变
- 引用类型:修饰地址值,地址不可以被改变,地址指向的数据可以改变
其他关键字
this关键字
-
表示该类的属性变量:this.变量名
-
调用本类的构造方法:this()
-
表示当前类,可调用成员方法:this.成原方法名()
new关键字
调用类的构造器(构造方法)
关于构造器
- 调用构造器时,若未写空构造器,系统会默认分配一个空构造器。
- 构造器的重载
- 重载一般要保证写好空构造器:若未写,系统不会默认分配,调用就会报错
- 构造器重载便于为属性赋值(可因对象不同值不同)
- 调用构造器前,对象已创建好,并且各属性已有默认的初始化的值
- 构造器格式(无参)
<权限修饰符> 构造器名() {
}
-
构造器和方法的区别:
- 没有方法的返回值类型
- 方法体内部不能有return语句
- 构造器名字 == 类名 (必须)
-
构造器的作用
为类的属性赋值(无参构造器或构造器的重载)
instanceof关键字
-
格式:
a instanceof A:判断对象a是否使A的实例。返回值为bool型(是返回true)
-
用法:
可避免向下转型时出现ClassCastException的异常,即先判断返回true再进行转型。
(换言之,可以对某个对象进行选择性的转型)
继承
-
访问父类的成员变量:super.变量名
-
关键字 访问成员变量 访问构造方法 this this.成员变量
访问本类成员变量this()
访问本类构造方法super super.成员变量
访问父类成员变量super()
访问父类构造方法 -
通过子类对象访问一个方法
- 先在子类范围内寻找
- 再在父类范围寻找
-
-
子类中所有构造方法被调用时,默认都会先访问父类无参构造方法
- 因为子类会继承父类中的数据,可能还会使用父类中的数据。所以子类初始化之前,一定要先完成父类数据的初始化
- 每一个子类构造方法的第一条语句默认都是super()
- 若使用父类带参构造方法进行数据初始化,即super()
- 若父类中没有无参构造方法,只有带参构造方法
- 用super关键字去显示的调用父类带参构造方法
- 在父类中自己提供一个无参构造方法
-
方法重写
-
子类出现了和父类中一模一样的方法声明
-
应用
-
当子类需要父类的功能,而功能的主体子类又自己特有的内容时,可重写父类中的方法,这样即沿袭了父类的功能,又定义了子类特有的内容。
-
重写的方法中,可擅用super关键字沿袭父类的(基础)功能
-
课用@Override检测重写方法是否有误
-
注意事项
-
父类中的private方法无法被子类继承,则无法被重写
-
子类方法访问权限不能更低
(public>默认>protected>private)
-
-
-
Java继承的注意事项
- 一个子类只能单继承,不支持多继承
- 可实现多层继承
-
多态
-
在Java中主要有两种体现形式:
- 方法的重载与覆写(重写)
- 对象的多态性
-
成员访问特点:(为何两种不一样:成员方法有重写)
- 成员变量:编译与执行均看左边
- 成员方法:编译看左边,执行看右边
-
优弊
-
优势:提高了程序的扩展性
具体体现:定义方法的时候,使用父类型作为参数,将来在使用的时候,使用具体的子类型参与操作
-
弊端:不能使用子类的特有功能
-
-
转型(实现使用子类特有功能)
-
向上转型:从子到父,父类引用指向对象
-
向下转型:从父到子,父类引用转为子类对象
用强制类型转换实现执行子类特有方法(不占用新的堆内存)
-
抽象类
-
概述:
在Java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象方法
-
特点
-
抽象类和抽象方法必须用abstract关键字修饰
-
抽象类中不一定有抽象方法,由抽象方法的类一定时抽象类
-
抽象类不能直接实例化
如何实例化:参照多态的方法,通过子类对象实例化,这叫做抽象类多态
-
抽象类的子类
要么重写抽象类中的所有抽象方法
要么本身就是抽象类
-
-
抽象类成员特点
-
成员变量:变量/常量
-
构造方法:
不可直接实例化。
用于子类访问父类数据的初始化 -
成员方法:
可以有抽象方法:限定子类必须完成某些动作
也可以有非抽象方法:提高代码的复用性
-
-
抽象类名作为形参和返回值
- 方法的形参时抽象类名,其实需要的时该抽象类的子类对象
- 方法的返回值是抽象类名,其实返回的是该抽象类的子类对象
接口
-
Java中的接口更多的体现在对行为的抽象
-
声明接口关键字:interface
public interface <接口名>;
调用接口关键字:implements
public class A implements <接口名>;
接口是abstract的,亦不可直接实例化
-
接口多态
- 多态的形式:具体类多态,抽象类多态,接口多态
- 多态的前提:
- 有继承或者实现关系;
- 有方法的重写/覆写
- 有父类/父接口引用指向子/实现类对象
-
成员特点
- 成员变量:默认被public static final修饰
- 成员方法:public abstract
-
构造方法
接口无构造方法,因为接口主要是行为进行抽象的
一个类如果无父类,默认继承自Object类
-
类和接口的关系
一个类或接口可以implements多个接口
-
抽象类和接口的关系
-
成员区别
抽象类 变量,常量;有构造方法;有抽象方法也有非抽象方法 接口 常量;抽象方法 2.关系区别
类与类 只能extends一个父类 类与接口 可以implements多个接口 接口与接口 同上 3.设计理念区别
抽象类 对象抽象(属性,行为) 接口 主要对行为抽象
-
-
接口名作为形参和返回值
- 方法的形参是接口名,其实需要的是该接口的实现类对象
- 方法的返回值是接口名,其实返回的是该接口的实现类对象
-
JDK8接口的新特性
-
JDK7及以前,只能定义全局常量和抽象方法(参见4.)
-
JDK8以后,接口还可以定义静态(static)方法和默认(default)方法。
-
接口中定义的静态方法,只能通过接口调用
如
interface IA() { public static method() {} } public SubClass implements IA {} public Class SubClassTest { public static void main(String[] args) { SubClass s = new SubClass(); s.method(); // × SubClass.method(); // × //上述两种调用方法,(编译时?)会完全无视method方法 IA.method(); // √ } }
-
接口中的默认方法,可通过实现类的对象调用。
-
如果子类(或实现类)继承的父类和实现的接口中声明了同名同参的方法,那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参的方法(类优先原则)
-
如果实现类实现了多个含有同名同参的默认方法的接口,且实现类没有重写此方法的情况下,会报错(接口冲突)(必须重写此方法才可以使用)
-
在子类(实现类)调用接口中的方法
class SuperClass() { public void method(); } interface CompareA() { public static void staticMethod(){} public void method(); } class SubClass extends SuperClass implements CompareA() { public void method(); public void myMethod() { method();//调用自己定义的重写的方法 super.method();//调用父类中声明的方法 CompareA.staticMethod();//调用接口中的静态方法 CompareA.super.method();//调用接口中的默认类型方法 } }
-
-
内部类
-
访问特点
- 内部类可直接访问外部类的成员,包括private
- 外部类要访问内部类成员,必须创建对象调用
-
成员内部类
-
当内部类权限为public时:
外界使用内部类格式(外部类名.内部类名):
例:Outer.Inner oi = new Outer().new Inner();
-
当内部类权限为private时:
类似于构建getAge和setAge方法。
-
-
局部内部类
-
定义在方法内,外界无法直接访问,需要在方法内部创建对象并使用
-
该类可直接访问外部类的成员,也可访问方法内的局部变量
-
匿名内部类
-
前提:存在一个类或接口,类可以时具体类也可抽象类
-
格式:
new 类名或接口名() { 重写方法; }
-
本质:
是一个继承了该类或实现了该接口的子类匿名对象
-
用例:
不用匿名内部类
interface A { public void printlnfo(); } class B implements A { //定义接口实现类 public void printlnfo() { //重写抽象方法 System.out.println("Hello World!"); } } class X { public void fun1() { this.fun2(new B()); } public void fun2(A a) { a.printlnfo(); } } public class NoInnerClassDemo01 { public static void main(String[] args) { new X().fun1(); } }
使用内部类改写:省去写新的实现接口的类
interface A { public void printlnfo(); } class X { public void fun1() { this.fun2(new A() { //匿名内部类 public void printlnfo() { //实现接口内中的抽象方法 System.out.printlnfo("Hello World!"); } }) } } public class NoInnerClassDemo02 { public static void main(String[] args) { new X().fun1(); } }
-
-
MVC设计模式
-
MVC将整个程序分为三个层次:视图模型层,控制器层,与数据模型层。
-
特点:
将程序输入输出、数据处理,以及数据的展示分离
-
优势:
使程序结构变的灵活而清晰,同时也描述了程序各个对象间的通信方式,降低了程序的耦合性。
-
各层次:
-
模型层 model 主要处理数据
>数据对象封装 model.bean/domain
>数据库操作类 model.dao
>数据库 model.db
-
控制层
>应用界面相关 controller.activity
>存放fragment controller.fragment
>显示列表的适配器 controller.adapter
>服务相关的 controller.service
>抽取的基类 controller.base
-
视图层
>相关工具类 view.utils
>自定义 view.ui
-
==与equals()
-
对于==运算符(比较的表达式类型必须一致)
- 比较基本数据类型变量:比较两个变量包存的数据是否相等
- 比较引用数据类型变量:比较两个对象的地址值是否相同,即两个引用是否指向同一个对像实体
-
对于equals()方法
-
只用于引用数据类型
-
源自Object类,其中equals ()的定义:
public boolean equals(Object obj) { return (this == obj); }
说明:和 == 的作用相同,比较两个对象的地址值是否相同。
-
其他类自带重写:如String, Data, File, 包装类等
比较两个对象的实体内容是否相同
-
String类中的equals()方法重写代码:
(可用于重写Object类的equals()方法)
public boolean equals(Object obj) { if (this == anObject) { return true; } if(anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while(n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }
-
toString()方法
-
源自Object类
其中对toString()的定义:
public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); }
-
String, Data, File, 包装类等都重写了Object类中toString()方法
返回实体内容信息
包装类
-
作用:将基本类型视为引用类型(对象)
-
基本类型 对应的引用类型 boolean java.lang.Boolean byte java.lang.Byte short java.lang.Short int java.lang.Integer long java.lang.Long float java.lang.Float double java.lang.Double char java.lang.Char 除java.lang.Boolean和java.lang.Char外,其余类的父类为Number
-
包装类,基本数据类型,String三者间的转换
-
基本数据类型—->包装类
调用包装类的构造器:
传入参数为代转换基本数据类型值
-
包装类—->基本数据类型
调用包装类的xxxValue()方法,如:
Integer in1 = new Integer(10); int i1 = in1.intValue();
-
自动装箱与自动拆箱:
省去调用构造器的过程
- 自动装箱如:
int in1 = 10; Integer in2 = in1;//自动装箱
- 自动拆箱如:
Integer in1 = new Integer(10); int in2 = in1;//自动拆箱
-
基本数据类型/包装类 —-> String类型
如:
int num1 = 10; //方式1:连接运算 String str1 = num1 + ""; //方法2:调用String的valueof()方法 float f1 = 12.3f; String str2 = String.valueof(f1);
-
String类型 —-> 基本数据类型、包装类
-
不能采用强制类型转换的方式(无子父类关系)
-
调用parsexxx()方法;
如:
String str = "string"; int num = Integer.parseInt(str);//等号右边返回值即为int类型 //但上述str会导致报错:NumberFormatException
-
-
-
关于包装类wrapper使用的面试题
-
Object o1 = true ? new Integer(1) : new Double(2.0); System.out.println(o1);
思考输出结果。 1.0
解释:三元运算符会在编译时统一左右两边的数据类型,故输出结果为double
-
Integer m = 1; Integer n = 1; System.out.println(m == n); //输出结果为true
Integer m = 128; Integer n = 128; System.out.println(m == n); //输出结果为false
解释:自动装箱时会调用Integer类中的一个类叫IntegerCache,里面有个数组叫Integer cache[];默认存储 -128 到 127 int数值。
而在该范围内比较数组中的地址是缓存里同一个数组Integer cache[]
若超出范围则new新的,故地址是不同的
-
单例(Singleton)设计模式
采取某种方法,使某个类只能有一个实例对象
-
饿汉式
class Demo01 { //1.私有化类的构造器 private Demo01() { } //2.内部创建类的对象 //静态方法只能调用静态对象 private static Demo01 instance = new Demo(); //3.提供公共的静态的方法(方便外部获取单例对象),返回类的对象 public static Demo01 getInstance() { return instance; } }
-
懒汉式
class Demo02 { 1.私有化类的构造器 private Demo02() { } //2.声明当前类对象(静态)(无初始化) private Demo02 instance = null; //3,声明公共的静态的返回当前类对象的方法 public static Demo02 getInstance() { if (instance == null) { //增加判断条件,保证instance严格只有一个 instance = new Demo02(); return instance; } } }
-
对比
-
饿汉式:
利:线程安全
弊:对象一开始就创建,(加载时间长)
-
懒汉式:
利:用的时候再创建对象
弊:目前写法,线程不安全:如,在用户A进入if内,instance还未被new的时候发生线程堵塞,用户B可以在此时也进入if内,则两者分别new了一次。
-
-
单例模式的优点:
由于单例模式只生成一个实例,减少了系统性能的开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。
如java.lang.Runtime:
public class Runtime { private static final Runtime currentRuntime = new Runtime(); private static Version version; public static Runtime getRuntime() { return currentRuntime; } /* Don\'t let anyone else instantiate this class */ private Runtime() {} ...... }
-
其他应用场景
- 网站的计数器,一般也是单例模式实现,否则难以同步。
- 应用程序的日志应用,一般都使用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
- 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。
- 项目中,读取配置文件的类,一般也只有一个对象。没有必要每次使用配置文件数据,都生成一个对象去读取。
- Application也是单例的典型应用
- Windows的Task Manager(任务管理器)就是很典型的单例模式
- Windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。
异常处理
-
概述:
规避一些非代码问题如,客户输入数据的格式,读取文件是否存在,网络是否始终保持通常等等
语法错误和逻辑错误不是异常。
分类:
- Error:JVM无法解决的严重问题,如JVM系统内部错误,资源耗尽等严重情况。如,StackOverflowError和OOM(堆溢出)。一般不编写针对性的代码进行处理。
- Exception:其他因编程错误或偶然的外在因素导致的一般性问题,可以用针对性的代码进行处理。如空指针访问,试图读取不存在的文件,网络连接中断,数组下标越界等。
-
常见异常,参见https://www.jianshu.com/p/a03c8807bbbc
-
异常处理模型:抓抛模型
-
过程一:“抛”,程序在正常执行过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象。并将此对象抛出。一旦抛出对象,try内其后的代码就不再执行。
-
过程二:“抓”,可以理解为异常的处理方式:
-
try-catch-finally
- 格式:
try{ //可能出现的代码 }catch(异常类型1 变量名1){ //处理异常的方式1 }catch(异常类型2 变量名2){ //... }//可多个catch ...... //可选是否使用finally finally{ //一定会执行的代码 }
-
要点:
-
finally可选
-
try内代码出现异常,会生成一个对应异常类的对象,根据此对象的类型,去catch中匹配。
-
匹配到catch后进行异常处理,完成后寻找fianlly执行(也可能无),随后进行执行后面的代码。
-
catch中的异常类型若无子父类关系,则上下顺序无所谓。
若有子父类关系,要求子类一定声明在父类上面,否则会报错。
-
常用的异常对象处理方式:
- String getMessage()
- printStackTrace()
-
在try结构中声明的变量,等结束try的使用后,就不能再被调用!
-
可以嵌套
-
使用try-catch-finally处理编译时异常,相当于将一个编译时可能出现的异常,延迟到运行时出现。
-
由于运行时异常比较常见,所以我们通常不针对运行时异常编写try-catch-finally了。针对编译时异常,我们一定要考虑异常的处理。
-
-
throws + 异常类型
-
写在方法声明处,指明此方法执行时,可能会抛出的异常类型。一旦方法执行时,出现异常,仍会在异常代码处生成一个异常类的对象。此对象满足throws后异常类型时,就会被抛出。异常后续的代码不再执行。
-
throws的方式只是将异常抛给了方法的调用者,并没有真正将异常处理掉。
真正处理掉的时try-catch-finally的方式
-
-
-
如何选择两种方式
- 如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws。意味着如果子类重写的方法中有异常,必须使用try-catch-finally方式处理。
- 执行的方法a中,先后又调用的另外的几个方法,这几个方法时递进关系执行的,则可以使用throws的方式进行处理,而执行的方法a可以使用try-catch-finally方式。
-
-
手动抛出异常(throw)
-
区别于系统自动生成的异常类型如FileNotFoundException等。
-
例子
public class StudentTest { try { Student s = new Student(); s.regist(-1001); System.out.println(s); } catch (Exception e) { //e.printStackTrace(); System.out.println(e.getMessage)); } } class Student { private int id; public void regist(int id) throws Exception { if (id > 0) { this.id = id; }else{ //System.out.println("您输入的数据非法!"); //手动抛出异常: //throw new RuntimeException("您输入的数据非法!")//不用在实现类里使用try-catch,因为类型为“运行时的错误“ throw new Exception("您输入的数据非法!")//需要在实现类里加try-catch,因为Exception类型考虑了编译时出现的问题。 } } }
-
-
用户如何自定义异常
- 继承于现有的异常结构:RuntimeException, Exception。
- 提供全集变量类序号:serialVersionUID(static final long)
- 提供重载的构造器