一、java中的数据类型
java中共分为四类八种基本数据类型:
-
整型:
byte: -128~127(-2的7次方到2的7次方-1)
short:-32768~32767(-2的15次方到2的15次方-1)
int:-2147483648~2147483647(-2的31次方到2的31次方-1)
long:-9223372036854774808~9223372036854774807(-2的63次方到2的63次方-1) -
浮点型:
float:3.402823e+38~1.401298e-45(e+38 表示乘以10的38次方,而e-45 表示乘以10的负45次方)
double:1.797693e+308~4.9000000e-324(同上) -
字符型:char
-
布尔型:boolean
下面是测试代码,重点看注释!
public class DataType {
public static void main(String[] args) {
// 整型(整数)
byte a = 127;
short b = 32767;
int c = 2147483647;
long d = 9223372036854774807L; // long类型后面要加上L,不加就会将此数按照int来处理
//浮点型(小数)
float e = 3.14f; // 默认情况下,小数都是double类型的,若要声明为float类型,则后面要加f (和上面的long类似)
double f = 4.4567;
//字符型 (Java开发需尽量避免使用char类型,
// 原因:char使用utf-16编码,改编码方式最多65536种且此编码已淘汰,java的Unicode编码后来新增了很多,
// 所以导致新增的Unicode需要用两个char来表示一个Unicode,这样会导致程序可能出现未知的错误)
char g = '国';
// what's 编码?
// 计算机底层是2进制的只认识1和0,所以我们制定了许多编码,
// 比如最初的ASCII,大写字母A 对应65,小写a 97 等,用这些编码协议来使计算机认识并输出我们需要的文本
//布尔型
boolean flag = true;
// 数据类型大小排列顺序:
// double > float > long > int > short > byte
int b1 = 128;
long b3 = b1; // 小的数据类型转大的,自动转换
// 大的转小的(或者char和其他6种数据类型转换),必须强制转换,
// 但是要考虑内存溢出,比如b1是128,但是byte范围是-128-127,此时强转就会内存溢出,导致b2的值出错
byte b2 = (byte) b1;
}
}
二、面向对象
面向对象的三大特征:封装、继承、多态。
封装:通常认为封装就是把数据和操作数据的方法绑定起来,对外部只提供一个定义好的接口。封装就是隐藏一切可隐藏的东西,只向外界提供最简单的编程接口。
继承:继承是从已有类得到继承信息创建新类的过程。
public class B {
public void test(){
System.out.println("B-->test()");
}
}
public class A extends B{
// 方法重写: 静态方法不存在重写,重写和静态方法无关,
// 重写也是多态的一种
@Override
public void test(){
System.out.println("A-->test()");
}
}
public static void main(String[] args) {
A a = new A();
a.test();// A继承了B,重写了test方法,所以这里会输出:A-->test(),若A不重写,则调用的就是B的test方法,输出B-->test()
B b = new A(); // 此处是向上转型(自动,A转为B ),向下转型需要强转
b.test(); // 输出:A-->test()
}
多态:多态性是指允许不同子类型的对象对同一消息作出不同的响应。说的再简单一些就是,用同样的对象调同样的方法,但是却做了不同样的事情(其实就是一个声明的父类有不同的子类实现,或者一个接口有多个实现类,所以相同的父类对象或接口对象调同样的方法,因为实例化的子类或实现类不同,所以做出了不同响应)。多态指的是方法的多态。
要实现多态的必要条件:
- 方法重写
- 父类引用指向子类对象(Animal a = new Cat(),向上转型)
三、static关键字、抽象类、接口、内部类
static关键字:可以用来修饰成员变量和成员方法,被static修饰的成员变量或方法是属于类的,不单单属于某个对象,所以这些成员变量或方法是可以直接通过 类名. XXX 来访问的。
注意:被static修饰的成员变量是属于类的,之后创建的每个对象都共享这同一个类变量的值,任何对象也都可以修改此变量值。
/**
* 此类为static关键字的详解
*/
public class StaticDemo {
/**
* 执行顺序:
* 静态代码块执行--
main方法执行--
非静态代码块执行--
构造方法执行--
*/
// 被static修饰的变量和方法,可通过类名直接调用
static final double PI =3.14;
{
System.out.println("非静态代码块执行--");
}
// 静态代码块:随着类的加载而执行,而且只执行一次
static {
System.out.println("静态代码块执行--");
}
public StaticDemo() {
System.out.println("构造方法执行--");
}
public static void main(String[] args) {
System.out.println("main方法执行");
StaticDemo staticDemo = new StaticDemo();
}
}
抽象类:抽象类中可以没有抽象方法,但是有抽象方法的类必须是抽象类。继承该抽象类的子类,必须重写抽象方法。抽象方法其实就是一种约束。
public abstract class AbstractDemo {
// 抽象方法可以当做是一种约束吧!
public abstract void test();
}
public class Z extends AbstractDemo{
@Override
public void test() {
System.out.println("zzzzzz");
}
}
public static void main(String[] args) {
AbstractDemo abstractDemo = new Z();
abstractDemo.test();
}
接口:java中的接口其实就是一堆抽象方法的集合,它和类不同,接口里主要是方法,接口无法实例化,但是可以被某个类实现它。简单来说接口其实就是一组定义好的规则,其他类去实现这些接口,重写里面的所有方法。一个类可以实现多个接口。
public interface UserService {
public void addUser();
}
public interface PersonService {
}
// 一个类可以实现多个接口,这也就弥补了java无法多继承这一特点了
public class UserServiceImpl implements UserService,PersonService {
@Override
public void addUser() {
System.out.println("增加一个用户--");
}
}
public class ServiceTest {
public static void main(String[] args) {
// 接口引用指向实现类,这也是多态的提现了
UserService userService = new UserServiceImpl();
userService.addUser();
}
}
内部类:内部类中又分为普通内部类、局部内部类、静态内部类、匿名内部类等,这些内部类认识即可,一般开发中不会用到,一些框架的源码中可能会用到。其中匿名内部类在lambda表达式中使其写法获得了精简,所以匿名内部类需要掌握。
// 以下的这些各种内部类了解即可,开发中一般来说用不到,但是要能看懂,有些框架的源码里可能会有
public class Outer {
private int id = 9527;
public void out(){
System.out.println("这是外部类的方法");
}
// 内部类
public class Inner{
public void in(){
System.out.println("这是内部类的方法");
}
public void getOuterID(){
System.out.println("内部类,可以获取到外部类的私有属性:id="+id);
}
}
public void in00(){
// 局部内部类,在方法里创建
class Inner00{
}
}
// 静态内部类
public static class Inner02{
public void in(){
System.out.println("静态内部类,无法获取外部类id,因为静态类是最先执行的");
}
}
}
// 这也是一个类,但注意同一个.java文件下只能有一个public修饰的类
class Inner03{
public void in03(){
System.out.println("Inner03");
}
}
匿名内部类的实现:
public class OutTest {
public static void main(String[] args) {
Outer out = new Outer();
// 内部类的创建方式
Outer.Inner inner = out.new Inner();
inner.getOuterID();
Inner03 inner03 = new Inner03();
inner03.in03();
// 匿名内部类,也就是只有实例,没有对应的变量(也就是栈里没变量名,堆里有实例)
new Outer().out();
UserService userService = new UserService() {
@Override
public void add() {
System.out.println("增加一个用户");
}
};
userService.add();
}
}
四、异常
java中的异常结构图如下:
可以看到,所有的异常根类为Throwable,下面分为Error和Exception,其中Error表示应用程序本身无法克服和恢复的一种严重问题;Exceotion表示程序还能够克服和恢复的问题,它下面又分为两种异常,运行时异常RuntimeException和非运行时异常(也叫编译时异常)CheckedException,其中CheckedException是必须要解决的,否则无法编译。
public class ExceptionTest {
// throws 用在方法声明后面,throws表示可能会出现的异常,但是并不一定会发生这种异常
public static void main(String[] args) throws Exception{
int a =1 ;
int b =0;
// 快捷键:ctrl+alt+t
try {
if(b==0){
// throw 主动抛出异常,用在方法体内
throw new ArithmeticException();
}
System.out.println(a/b);
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("finally代码块执行");
}
}
}
自定义异常:java虽然本身提供了丰富的异常处理类,但有时候这些异常类还是满足不了开发中一些特定的需求,所以需要自定义异常。自定义异常的写法就是编写一个类继承Exception类,那这个类就属于一个自定义异常类了。
// 自定义异常类
public class MyException extends Exception{
private int detail;
public MyException() {
}
public MyException(int a) {
this.detail = a;
}
// 自定义异常必须重写toString方法,toString方法也就是对该异常的描述
@Override
public String toString() {
return "触发了自定义异常:MyException{" +
"detail=" + detail +
'}';
}
}
// 测试自定义异常
public class Test {
public static void main(String[] args) {
try {
test(6);
} catch (MyException e) {
e.printStackTrace();
System.out.println(e); // 重写了toString方法,所以可以直接输出e
}
}
static void test(int a) throws MyException{
// 当a大于5时,抛出自定义异常
if(a>5){
throw new MyException(a);
}
}
}
常见的一些RuntimeException:
- NullPointerException 空指针异常
- ClassNotFoundException 指定的类找不到
- ClassCastException 数据类型转换异常
- SQLException SQL 异常
- IndexOutOfBoundsException 数组角标越界异常
- NoSuchMethodException 方法不存在异常
五、注解
注解,顾名思义,就是对某一事物进行添加注释说明,会存放一些信息,这些信息可能对以后某个时段来说是很有用处的。
Java注解又叫java标注,java提供了一套注解机制,使得我们可以对方法、类、参数、包、域以及变量等添加标准(即附上某些信息)。且在以后某个时段通过反射将标注的信息提取出来以供使用。
简单来说,注解可以放到类、方法、参数等位置上,表示对这些域或变量添加一些标准一些信息。然后通过java中的反射机制可以将这些注释内容提取出来并使用。可以说注解是反射机制的一种实现方式。
元注解:@Target、@Retention、@Documented、@Inherited 就是负责注解其他注解
/**
* 注解的作用:
* 1.不是程序本身,可以对程序作出一些解释,和注释差不多
* 2.可以被其他程序(如编译器等)读取
*
* 其实注解就是根据反射来得到注解的内容,从而对程序做出相应的响应,
* 注解也就是反射的一种应用场景
*
* 以下4个是元注解,元注解的作用就是负责注解其他注解
*/
@Target(ElementType.METHOD)//表示注解可以用在哪些地方(方法上、类上等,可以写多个值)
@Retention(RetentionPolicy.RUNTIME)// 表示注解在哪些地方有效,runtime(运行时)>class(class文件)>sources(源码中)
@Documented // 表示是否将我们的注解生成在javadoc中
@Inherited // 子类可以继承父类的注解
public @interface YuanAnno {
}
class Test{
@YuanAnno
public void test(){
}
}
自定义注解: 可以用@interface来声明一个自定义注解
@Target(value = {ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomAnno {
// 注解中的参数怎么写? 参数类型+参数名(); 和类中的参数定义相比多了一个()
String name() default "";
int age();
int id();
String[] arr() default {"勇士", "篮网"};//可以给参数一个默认值
}
class Test02 {
// 如果注解中的参数没有默认值,这里就必须写参数,若有,可以不写
@CustomAnno(id = 35, age = 23)
public void test() {
}
}
六、反射(重点)
反射是java中很重要的一个特性,java中的所有框架mybatis、spring、springmvc等全部运用了反射,反射是构建框架的基础也是精髓所在!反射在日常开发中不经常用到,是因为框架已经帮我们做好了,学好反射是理解框架底层原理的必要条件。
反射,顾名思义,‘反’过来的,那与之相对的就是‘正’,首先要先理解正是什么?
在java中我们创建一个对象一般都是:
Apple apple = new Apple(); //直接初始化,「正射」
apple.setPrice(4);
这里创建对象的过程就是正,首先在编写这段代码前,因为我们知道我们需要一个Person类,所以我们主动去创建new一个Person类,并调用该类的方法。new 的这个动作就是我们主动创建的,这也就是正。
而反射,就是与正相对,我们在编写代码前并不知道要创建什么类,但问题是我还需要对即将创建的这个类做一些事情,这个时候就需要用到反射。
Class clz = Class.forName("com.hhl.reflect.Apple");
Method method = clz.getMethod("setPrice", int.class);
Constructor constructor = clz.getConstructor();
Object object = constructor.newInstance();
method.invoke(object, 4);
从以上两种正和反可以看出,普通的创建对象,是我们在编译期就知道需要哪个类,所以直接创建了相应的类,而反射是在编译期并不知道需要哪个类,是在程序运行时动态去加载创建类的。
其实看到上面反射的代码有的人会有疑问,我获取Class对象时已经把需要的类的路径写好了,这不就说明在编译期我已经知道要创建Apple类了吗,为啥还通过反射,岂不是多此一举吗?
- 这里要说明的是,上面的反射代码是为了让我们理解而反射而写的,首先这个com.hhl.reflect.Apple里的这个Apple类可能在我们编写这段反射代码时还没写好(或者说这个Apple类是另一个程序员在写),但是我现在又必须用到这个Apple类,那怎么办?就可以用反射来创建对象并应用。
- 以Spring框架IOC为例,都说spring是一个容器,那么spring是如何帮我们管理并实例化bean的呢?可以在对象上面加注解@Compent或者在配置文件中写:
<bean id="courseDao" class="com.qcjy.learning.Dao.impl.CourseDaoImpl"></bean>
spring帮我们实例化并管理bean是在我们程序启动时,运行时去实例化的,但程序运行时spring并不知道我们项目哪些类需要被它管理,所以我们要写配置文件或者注释告诉spring哪些类需要被它管理,然后spring通过这些配置文件中写的bean路径,再根据反射的特性就可以用反射来实例化对象并管理。 这也就是为什么说框架是半成品软件,因为是需要我们的配置+框架=项目。所以说反射是框架设计的精髓,没有反射,Spring框架就无法在程序运行时动态的去实例化对象并管理。
上面只是解释了反射在Spring框架中的一个提现,框架用到反射的地方还有很多很多,可以说没有反射就没有框架的诞生了。
总结:java本身是一门静态语言,但是通过java反射的机制,使得java变成了一门准动态语言。反射最重要的特点就是在程序运行时动态的创建对象和编译,并可以获取该类的所有属性和方法。
Class类:
先要理解一个概念,一个类可以创建多个对象,但是一个类在java内存中只有一个class对象,这个class对象包含了这个类的一切信息。Class对象是java反射的起源,反射机制的实现就是首先获取某个类的Class对象,通过该Class对象对应的API 可以获取该类下的所有内容(变量、方法等一切内容,包括私有变量和方法也都可以获取到)。
获取Class对象的3种方式:
- Class.forName(“类路径”)
- 类名.class
- 对象.getClass() 方法
下面是反射中常用到的Class对象的一些API:
public class Test {
public static void main(String[] args) throws Exception {
// 获取class对象的3种方式
Class c1 = Class.forName("com.hhl.reflection.Student");
Class c2 = Student.class;
Student student = new Student();
Class c3 = student.getClass();
// 根据class对象,获取类的相关信息
System.out.println(c3.getName()); // 包名+类名
System.out.println(c3.getSimpleName());// 类名
Field[] fields = c3.getFields(); // 获取public属性
Field[] declaredFields = c3.getDeclaredFields(); // 获取到类的所有属性,包括私有的
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
Field name = c3.getDeclaredField("name");// 获取指定属性
System.out.println(name);
Method[] methods = c3.getMethods(); // 获取所有public方法
Method[] declaredMethods = c3.getDeclaredMethods();// 获取所有方法,包括私有的
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
// 获取指定方法
Method getAge = c3.getMethod("getAge", null); //无参数,就是null
Method setAge = c3.getMethod("setAge", int.class);// 有参数,必须写参数class对象,因为要区分方法重载
System.out.println(getAge);
System.out.println(setAge);
// 获得类的构造器,也就是构造方法
Constructor[] constructors = c3.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
// 获取指定有参构造器
Constructor constructor = c3.getConstructor(int.class, String.class, int.class);
System.out.println(constructor);
}
}
利用反射机制,创建实例对象、执行方法:
public class Test02 {
public static void main(String[] args) throws Exception {
Class c1 = Class.forName("com.hhl.reflection.Student");
// 利用反射,class对象创建实例对象
Student s2 = (Student) c1.newInstance();
// 根据构造器创建对象
Constructor constructor = c1.getConstructor(int.class, String.class, int.class);
Student s3 = (Student) constructor.newInstance(35, "杜兰特", 7);
Method study = c1.getMethod("study",null);
// invoke,激活的意思, 执行方法,@param1 对象实例, @param2 方法参数
study.invoke(s2,null);
Field name = c1.getDeclaredField("name");
name.setAccessible(true);// 若字段或者方法是私有的,这里需要关闭安全检测,将Accessible设置为true
name.set(s2,"詹姆斯");
System.out.println(s2);
}
}
通过反射,获取注解和注解中的属性值内容(这也是框架中经常用到的,比如获取注解信息,然后生成自动的增删改sql):
/**
* 通过反射,获取注解信息
*/
public class Test03 {
public static void main(String[] args) throws Exception {
Class c1 = Class.forName("com.hhl.reflection.Person");
// 获取类上的所有注解
Annotation[] annotations = c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
// 获取注解的value值
LeiAoon leiAoon = (LeiAoon) c1.getAnnotation(LeiAoon.class);
String value = leiAoon.value();
System.out.println(value);
// 获取注解中某个单独的属性值
Field age = c1.getDeclaredField("age");
FiledAnno annotation = age.getAnnotation(FiledAnno.class);
System.out.println(annotation.filed());
System.out.println(annotation.type());
}
}
@LeiAoon("lg_person")
class Person{
@FiledAnno(filed = "age",type = 1)
private int age;
@FiledAnno(filed = "name",type = 2)
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Target(ElementType.TYPE)//作用在类上
@Retention(RetentionPolicy.RUNTIME)
@interface LeiAoon{
String value();
}
@Target(ElementType.FIELD)// 作用在属性字段上
@Retention(RetentionPolicy.RUNTIME)
@interface FiledAnno{
String filed();
int type();
}
java反射的优缺点:
优点:很多了,最大的优点就是提现在它的动态性上,可以实现运行时动态的创建对象和编译,使代码更加灵活。
缺点:就是对性能上有影响,且反射里的各种API比较多且复杂。
最后说一点,反射在平时开发中用到的不多,但是对于我们理解学习框架底层原理很重要,因为所有的框架几乎都用到了反射。