精华内容
下载资源
问答
  • MySQL数据库基础(必备技能)

    千次阅读 2018-10-17 17:19:46
    第一章:数据库的设计 1.第一范式: 第一范式的目标是确保每列的原子性,如果每列(或者每个属性值)都是不可再分的最小数据单元(也成为最小的原子单元),则满足第一范式 2.第二范式: 第二范式其目标是确保表中的...

    第一章:数据库的设计

    1.第一范式:
    第一范式的目标是确保每列的原子性,如果每列(或者每个属性值)都是不可再分的最小数据单元(也成为最小的原子单元),则满足第一范式

    2.第二范式:
    第二范式其目标是确保表中的每列都和主键相关,并且除了主键以外的其他列都全部依赖于该主键

    3.第三范式:
    第三范式的目标是确保每列都和主键列直接相关,而不是间接相关,并且除了主键以外的其他列都只能依赖于主键列,列与列之间不能存在相互依赖关系

    第二章:初始MySQL

    1.端口设置(默认启用TCP/IP网络,默认端口为3306)

    2.默认字符集设置(默认值为utf8)

    3.MySQL文件夹
    * bin文件夹:该文件夹下存放着可执行文件
    * include文件夹:该文件夹下存放着头文件
    * lib文件夹:该文件夹下存放着库文件
    * share文件夹:该文件夹下存放着字符集,语言等信息

    4.修改MySQL配置文件来手动配置数据库服务器MySQL时,常用参数:
    * default - character - set:客户端默认字符集
    * character - set - setver:服务器端默认字符集
    * port:客户端和服务器端的端口号
    * delault - storage - engine:MySQL默认存储引擎

    5.开服务:(net start mysql)

    6.关服务:(net stop mysql)

    7.命令行方式连接数据库
    语法:
    mysql -h 服务器主机地址 -u 用户名 -p 密码 (如果是本机可省略 -h)

    8.操作数据库
    1.创建数据库
    语法:
    create database 数据库名;

    2.查看数据库列表
      语法:
           show databases;
     
    3.选择数据库
      语法:
           use 数据库名;
    
    4.删除数据库
      语法:
           drop database 数据库名;
      
    5.创建表
      语法:
           create table if not exists 表名(
                 字段1 数据类型 字段属性 约束 索引 注释,
                 ...
           )表类型 表字符集 注释;
    
    6.单字段主键
      语法:
           create table if not exists 表名(
                  字段1 数据类型 primary key,
                  ...
           );
    
    7.查看表
      语法:
           show tables;
    
    8.查看表的定义
      语法:
           describe 表名;
            或
           desc 表名;
    
    9.删除表
      语法:
           drop table if exists 表名;
    

    9.如果需要以0填充不足的位置,需要在类型后添加zerofill关键字

    10.各数据类型都包含有符号数和无符号数两类,默认为有符号数,既可以保持负数,如该字段为非负数,则需添加unsigned属性

    11.数据类型
    常用数值类型

       数据类型                字节数
     tinyint[(m)]               1            
     smallint[(m)]              2
     mediumint[(m)]             3
     int[(m)]                   4
     float[(m,d)]               4
     double[(m,d)]              8
     decimal[m([,d])]          m+2
        bit             bit称为位数据类型,其数据有两种取值:0和1 长度为1位,用来表示真假,是否	
    

    12.字符串类型
    字符串类型

       数据类型              字节                说明 
       char[(m)]             m          固定长字符串,M为0-255的整数
     varchar[(m)]         可变长度      可变长度,M为0-65535的整数
      tinytext             0-255        微型文本串
        text              0-65535       文本串
     decimal(m,d)     该类型一般用于金额方面,m表示要显示的长度,d表示保留的小数位数 
    

    13.日期类型
    日期类型

       数据类型               格式
        date              yyyy-mm-dd                   
      datetime            yy-mm-dd hh:mm:ss:
        time              hh:mm:ss:
      timestamp           yyyymmddhhmmss
        year              yyyy              
    

    14.字段的约束及属性
    常用的属性约束

      字段属性,约束名          关键字
       非空约束               not null
       默认约束               default
       唯一约束               unique key(uk)
       主键约束               primary key(pk)
       外键约束               foreign key(fk)
       自动增长               auto_increment
    

    15.MySQL的存储引擎
    常用的存储引擎
    InnoDB 和 MyISQM存储引擎比较

        功能                InnoDB                 MyISAM
      支持事物                支持                  不支持 
      支持全文索引            不支持                 支持
      外键约束                支持                  不支持
      表空间大小              较大                   较小
      数据行锁定              支持                  不支持
    
      操作系统的默认存储引擎是 InnoDB
    
      查看存储引擎的语法:
         show variables like 'storage_engine%';
    

    16.指定表中的存储引擎
    语法:
    create table 表名(

    )engine=存储引擎;

    第三章:高级查询(一)

    1.修改表
    1.修改表名
    语法:
    alter table 原表名 rename 新表名

    2.添加字段
      语法:
          alter table 表名 add 字段 数据类型 属性
    
    3.修改字段
      语法:
          alter table 表名 change 原字段名 新字段名 数据类型 属性
    
    4.删除字段
      语法:
          alter table 表名 drop 字段名
    

    2.添加主外键
    1.添加主键约束
    语法:
    alter table 表名 add constraint 主键名 primary key 表名(主键字段)

    2.添加外键约束
      语法:
          alter table 表名 add constraint 外键名 foreign key(外键字段) references 关联表名 (关联字段)
    

    3.插入数据记录
    1.插入单行数据
    语法:
    insert into 表名 (字段列表) values(值列表)

    2.插入多行数据
      语法:
          insert into 表名 (字段列表) values(值列表1),(值列表2),...
    
    3.将查询结果插入到新表中
      语法1:
           insert into 新表 (字段1,字段2...) 
           select 字段1,字段2...
           from 表名
    
      语法2:
       create table 新表 (select 字段1,字段2,...from 表名)
    

    3.数据更新记录
    语法:
    update 表名 set 字段1=值1,字段2=值2,… where 条件

    4.删除数据记录
    语法1:
    delete from 表名 where 条件

    语法2:
    truncate table 表名
    

    5.数据查询
    语法:
    select <字段列表>
    from <表名或视图>
    where <查询条件>
    group by <分组的字段名>
    having <条件>(注意:该位置的条件是 筛选组)
    order by <排序的字段名> asc或desc

    6.limit子句
    语法:
    select <字段列表>
    from <表名或视图>
    where <查询条件>
    group by <分组的字段名>
    order by <排序的字段名> asc或desc
    limit 位置偏移量,行数

    7.常用函数
    1.聚合函数
    常用聚合函数

       函数名               作用  
       avg()           返回某字段的平均值
       count()         返回某字段的行数
       max()           返回某字段的最大值
       min()           返回某字段的最小值
       sum()           返回某字段的和
    
    2.字符串函数
                                      常用字符串函数
    
             函数名                            作用                         举例        
          concat(str1,str2,...strn)       连接完整字符串          select concat('My','S','QL')
                                                                 返回:MySQL 
          insert(str,pos,len,newstr)      替换                    select insert('这是SQL Server数据库',3,10,'MySQL') 
                                                                  返回:	这是MySQL数据库						                  lower(str)                      变小写                  select lower('MySQL')  							                                                          返回:mysql			
          upper(str)                      变大写                  select upper('mysql')  
                                                                  返回:MYSQL
          substring(str,num,len)          截取                    select substring('JavaMySQLOracle',5,5)
                                                                  返回:MySQL
    
    3.时间日期函数
                              常用日期函数
    
          函数名                   作用                         举例
        curdate()             获取当前日期                    select curdate() 
        curtime()             获取当前时间                    select curtime()
        now()                 获取当前日期和时间               select now()
        week(date)            返回日期date为一年中的第几周     select week(date)
        year(date)            返回日期date的年份               selct year(date)
        hour(time)            返回时间time的小时值             select hour(now())
        minute(time)          返回时间time的分钟值             select minute(now())
        datediff(date1,date2) 返回日期参数date1和date2相隔天数  select datediff(now(),'2008-8-8')
        adddate(date,n)       计算日期参数date加上n天后的日期   select adddate(now(),5)  
    
    4.数学函数
                 常用数学函数
    
       函数名                作用
       ceil(x)           返回大于或等于x的最小整数
       floor(x)          返回小于或等于x的最大整数
       and()             返回0-1间的随机数
    

    8.子查询
    语法:
    select …
    from 表1
    where 字段1比较运算符(子查询)

    9.in和not in子查询
    1.in子查询
    注意:如果条件后面是用等号跟的子查询,那么该查询就不能返回多个值
    使用in则相反

    2.not in子查询
      注意:in前面加not就是结果的反向值
    
    事列:
         #嵌套子查询事列
         SELECT re.`studentNo`
         FROM `result` re
         WHERE re.`subjectNo`NOT IN(
          		 SELECT su.`subjectNo` FROM `subject` su
       		 WHERE su.`subjectName`='HTML'					
    		) AND re.`examDate` IN(
    				 SELECT MAX(r.`examDate`)  FROM `result` r
    				 WHERE r.`subjectNo`NOT IN(
    	                         SELECT s.`subjectNo` FROM `subject` s
    			         WHERE s.`subjectName`='HTML'
    			        )					     
    			);
    

    第四章:高级查询(二)

    1.exists 和 not exists子查询
    1.exists 子查询
    语法:
    select … from 表名 where exists(子查询)

    2.not exists子查询
      exists 和 in一样,同样允许添加not关键字实现取反操作,not exists表示不存在
    
    3.嵌套在select语句的from子句中的子查询语法:
      select * from (子查询) as 表的别名
      注意:表的别名一定要写
    
    4.创建临时表
      语法:
           create temporary table 表名 (查询语句)
    
    5.联合查询
      语法:
           union
    
    6.把时间区分为季度的函数
      QUARTER()
      注意:括号里面存放想要区分的时间
    

    第五章:事务,视图,索引,备份和恢复

    1.事务
    事务是指将一系列数据操作捆绑成为一个整体进行统一管理

    1.事务特性
      1.原子性
        事务是一个完整的操作,事务的各元素是不可分的(原子性)
    
      2.一致性
        在事务开始之前,数据库中储存的数据处于一致状态
    
      3.隔离性
        对数据进行修改的所有并发事务是彼此隔离的,这表明事务必须是独立的,它不应以任何方式依赖于或影响其他事务
    
      4.持久性
        事务的持久性指不管系统是否发生了故障,事务处理的结果都是永久的    
    
    2.如何执行事务
      支持事务的存储引擎有InnoDB和BDB,InnoDB存储引擎事务主要通过UNDO日志和REDO日志实现
        * UNDB 日志:复制事务执行前的数据,用于在事务发生异常是回滚数据
        * REDO 日志:记录在事务执行中,每条对数据进行更新的操作,当事务提交时,该内容将被刷新到磁盘
    
    3.执行事务的语法:
    
      1.开始事务
      语法:
           begin 或 start transaction
    
      2.提交事务
      语法:
           commit 
    
      3.回滚(撤销)事务
      语法:
           rollback
    
      4.设置自动提交关闭或开启
      语法:
           set autocommit=0|1
            * 值为0:关闭自动提交
            * 值为1:开启自动提交  
    

    2.视图
    视图是保存在数据库中的select查询
    视图是一种虚拟表,通常 是作为来自一个或多个表的行或列的子集创建的

    1.使用sql语句创建视图
      语法:
           create view 视图名
           as
           <select语句>   
    
    2.使用sql语句删除视图
      语法:
           drop view if exists 视图名
    
    3.使用sql语句查看视图数据
      语法:
           select 字段 from 视图名
    

    3.索引

    1.索引的分类:

     1.普通索引
       普通索引是mysql中的基本索引类型,允许在定义索引的列中插入重复值和空值,它的唯一任务是加快对数据的访问速度
    
     2.唯一索引
       唯一索引不允许两行具有相同的索引值
    
     3.主键索引
       在数据库关系图中为表定义一个主键将自动创建主键索引,主键索引是唯一索引的特殊类型
    
     4.复合索引
       在创建索引时,并不是只能对其中一列创建索引,与创建主键一样,可以将多个列合作为索引
    
     5.全文索引
       全文索引的作用是在定义索引的列上支持值的全文查找,允许在这些索引列中插入重复值和空值,主要用于在大量文本文字中搜索字符串
    
     6.空间索引
       空间索引是对空间数据类型的列建立的索引
    

    2.创建索引
    语法:
    create unique|fulltext|spatial index 索引名
    on 创建索引的表名 (创建索引的表名的列名)
    * unique|fulltext|spatial:分别表示唯一索引,全文索引和空间索引

    3.删除索引
    语法:
    drop index 索引名 on 创建索引的表名

    4.查看索引
    语法:
    show index from 创建索引的表名

    4.数据库的备份和恢复

    1.使用mysqldump命令备份数据库
      语法:
           mysqldump -u 用户名(一般为root) -h 主机名(一般不写) -p 密码 要备份的数据库名 要备份的数据库中的表名 > 路径(例如:d:\1\java.sql)
    
    2.使用mysql命令恢复数据库
      语法:
           mysql  -u 用户名(一般为root) -h 主机名(一般不写) -p 密码 要给恢复的数据库名 < 路径(例如:d:\1\java.sql)
    
      注意:1和2一定记住是在dos命令下写的
    
    3.mysql命令是在dos环境下的恢复数据库命令,如果已经登录列MySQL服务器,也可以使用source命令恢复数据库
      语法:
           source 数据库备份文件(所谓的路径)
    
    4.将所有数据写入到数据文件中
      语法:
           flush tables
    
    5.表数据导出到文本文件
      语法:
           select 查询的列
           from 表名
           where 条件
           into outfile 路径(列如:'d:/1/java.txt')
    
    6.文本文件写入到数据表
      语法:
           load data infile 路径(列如:'d:/1/java.txt') into table 要导入的表名(注意:要导入的表名要提前创建好)
    
    7.知识扩展
      * fields 
          terminated by 'string':用来设置字段的分割符为字符串对象,默认为“\t”  
    
      * fields
          optionally | enclosed by'char':用来设置括上字段值的字符符号,如果使用了optionally,则只有char和varchar等字符数据字段被包括,
                                         默认情况下不使用任何符号  
    
      * fields
          escaped by'char':用来设置转义字符的字符符号,默认情况下使用“\”字符
    
      * lines
           starting by'char':用来设置每行开头的字符符号,默认情况下不使用任何符号
    
      * lines
           terminated by'string':用来设置每行结束的字符符号,默认情况下使用“\n”字符串  
    

    第七章:JDBC

    1.JDBC访问数据库的步骤

    1.加载JDBC驱动
      Class.forName("JDBC驱动类的名称")   注意:JDBC驱动类的名称一般为:com.mysql.jdbc.Driver
    
    2.与数据库建立连接
      Connection conn=DriverManager.getConnection(数据连接字符串,数据库用户名,密码)      注意:数据库连接字符串一般为:jdbc:mysql://本机名(一般为:localhost):3306/数据库名  数据库用户名一般为:root  密码一般为:root
    
    3.发送SQL语句,并得到返回结果
      一旦建立连接,就是用该连接创建Statement接口的对象,并将SQL语句传递给它所连接的数据库,如果是查询操作,将返回类型为ResultSet的结果集,它包含执行SQL查询的结果
      Statement stmt = conn.createStatement();
      ResultSet rs=stmt.executeQuery("select `id`,`name` from `master`")
    
    4.处理返回结果
      处理返回结果主要是针对查询操作的结果集,通过循环取出结果集中每条记录并做相应处理
    

    2.Statement接口和ResultSet接口

                 Connection 接口常用方法
    
        方法名称                         作用
      void close()                     释放资源
    Statement createStatement()        创建一个Statement对象来将SQL语句发送到数据库
      PreparedStatement
    preparedStatement(String sql)      创建一个PreparedStatement对象来将参数化的SQL语句发送到数据库
      boolean isClosed()               查询此Connection对象是否已经关闭
    
                 Statement 接口常用方法
    
        方法名称                           作用              
    ResultSet executeQuery(String sql)    可以执行SQL查询并获取ResultSet对象
      int executeUpdate(String sql)       执行增,删,改操作,返回影响行数
      boolean execute(String sql)         可以执行任意SQL语句,若结果为ResultSet对象,则返回true,反之为false
    
                    ResultSet 接口常用方法及作用
    
          方法名称                           作用          
     boolean next()                      下一行
     boolean previous()                  上一行
     void close()                        释放资源
     int getInt()                        int类型
     int getFloat()                      float类型
     String getString()                  String类型
     int getRow()                        得到光标当前所指行的行号
     boolean absolute(int row)           光标移动到row指定的行
    

    3.PreparedStatement接口
    PreparedStatement接口继承Statement接口

                   PreparedStatement 接口常用方法
    
          方法名称                           作用     
      boolean exectute()                 true或false
      ResultSet executeQuery()           查询
      int executeUpdate()                执行增,删,改操作,返回影响行数
      void setInt(int index,int x)       给参数
      void setObject(int index,Object x) 使用给定对象设置指定参数的值
    

    第八章:DAO模式

    1.Properties类
    properties配置文件

                  Properties类的常用方法
       
         方法                                         描述
     String getProperty(String key)               用指定的键在此属性列表中搜索属性,通过参数key得到其所对应的值
     Object setProperty(String key,String value)  调用Hashtable的方法put,通过调用基类的put()方法来设置键-值对
     void load(InputStream inStream)              从输入流中读取属性列表(键和元素对),通过对指定文件进行装载获取该文件中所以键-值对
     void clear()                                 清除所装载的键-值对,该方法有基类Hashtable提供
    
    展开全文
  • Java基础知识面试题(2020最新版)

    万次阅读 多人点赞 2020-02-19 12:11:27
    值传递 当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递 为什么 Java 中只有值传递 值传递和引用传递有什么区别 Java包 JDK 中常用的...

    Java面试总结(2021优化版)已发布在个人微信公众号【技术人成长之路】,优化版首先修正了读者反馈的部分答案存在的错误,同时根据最新面试总结,删除了低频问题,添加了一些常见面试题,对文章进行了精简优化,欢迎大家关注!😊😊

    【技术人成长之路】,助力技术人成长!更多精彩文章第一时间在公众号发布哦!

    文章目录

    Java面试总结汇总,整理了包括Java基础知识,集合容器,并发编程,JVM,常用开源框架Spring,MyBatis,数据库,中间件等,包含了作为一个Java工程师在面试中需要用到或者可能用到的绝大部分知识。欢迎大家阅读,本人见识有限,写的博客难免有错误或者疏忽的地方,还望各位大佬指点,在此表示感激不尽。文章持续更新中…

    序号内容链接地址
    1Java基础知识面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/104390612
    2Java集合容器面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/104588551
    3Java异常面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/104390689
    4并发编程面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/104863992
    5JVM面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/104390752
    6Spring面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/104397516
    7Spring MVC面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/104397427
    8Spring Boot面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/104397299
    9Spring Cloud面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/104397367
    10MyBatis面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/101292950
    11Redis面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/103522351
    12MySQL数据库面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/104778621
    13消息中间件MQ与RabbitMQ面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/104588612
    14Dubbo面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/104390006
    15Linux面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/104588679
    16Tomcat面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/104397665
    17ZooKeeper面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/104397719
    18Netty面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/104391081
    19架构设计&分布式&数据结构与算法面试题(2020最新版)https://thinkwon.blog.csdn.net/article/details/105870730

    Java概述

    何为编程

    编程就是让计算机为解决某个问题而使用某种程序设计语言编写程序代码,并最终得到结果的过程。

    为了使计算机能够理解人的意图,人类就必须要将需解决的问题的思路、方法、和手段通过计算机能够理解的形式告诉计算机,使得计算机能够根据人的指令一步一步去工作,完成某种特定的任务。这种人和计算机之间交流的过程就是编程。

    什么是Java

    Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程 。

    jdk1.5之后的三大版本

    • Java SE(J2SE,Java 2 Platform Standard Edition,标准版)
      Java SE 以前称为 J2SE。它允许开发和部署在桌面、服务器、嵌入式环境和实时环境中使用的 Java 应用程序。Java SE 包含了支持 Java Web 服务开发的类,并为Java EE和Java ME提供基础。
    • Java EE(J2EE,Java 2 Platform Enterprise Edition,企业版)
      Java EE 以前称为 J2EE。企业版本帮助开发和部署可移植、健壮、可伸缩且安全的服务器端Java 应用程序。Java EE 是在 Java SE 的基础上构建的,它提供 Web 服务、组件模型、管理和通信 API,可以用来实现企业级的面向服务体系结构(service-oriented architecture,SOA)和 Web2.0应用程序。2018年2月,Eclipse 宣布正式将 JavaEE 更名为 JakartaEE
    • Java ME(J2ME,Java 2 Platform Micro Edition,微型版)
      Java ME 以前称为 J2ME。Java ME 为在移动设备和嵌入式设备(比如手机、PDA、电视机顶盒和打印机)上运行的应用程序提供一个健壮且灵活的环境。Java ME 包括灵活的用户界面、健壮的安全模型、许多内置的网络协议以及对可以动态下载的连网和离线应用程序的丰富支持。基于 Java ME 规范的应用程序只需编写一次,就可以用于许多设备,而且可以利用每个设备的本机功能。

    JVM、JRE和JDK的关系

    JVM
    Java Virtual Machine是Java虚拟机,Java程序需要运行在虚拟机上,不同的平台有自己的虚拟机,因此Java语言可以实现跨平台。

    JRE
    Java Runtime Environment包括Java虚拟机和Java程序所需的核心类库等。核心类库主要是java.lang包:包含了运行Java程序必不可少的系统类,如基本数据类型、基本数学函数、字符串处理、线程、异常处理类等,系统缺省加载这个包

    如果想要运行一个开发好的Java程序,计算机中只需要安装JRE即可。

    JDK
    Java Development Kit是提供给Java开发人员使用的,其中包含了Java的开发工具,也包括了JRE。所以安装了JDK,就无需再单独安装JRE了。其中的开发工具:编译工具(javac.exe),打包工具(jar.exe)等

    JVM&JRE&JDK关系图

    什么是跨平台性?原理是什么

    所谓跨平台性,是指java语言编写的程序,一次编译后,可以在多个系统平台上运行。

    实现原理:Java程序是通过java虚拟机在系统平台上运行的,只要该系统可以安装相应的java虚拟机,该系统就可以运行java程序。

    Java语言有哪些特点

    简单易学(Java语言的语法与C语言和C++语言很接近)

    面向对象(封装,继承,多态)

    平台无关性(Java虚拟机实现平台无关性)

    支持网络编程并且很方便(Java语言诞生本身就是为简化网络编程设计的)

    支持多线程(多线程机制使应用程序在同一时间并行执行多项任)

    健壮性(Java语言的强类型机制、异常处理、垃圾的自动收集等)

    安全性

    什么是字节码?采用字节码的最大好处是什么

    字节码:Java源代码经过虚拟机编译器编译后产生的文件(即扩展为.class的文件),它不面向任何特定的处理器,只面向虚拟机。

    采用字节码的好处

    Java语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。所以Java程序运行时比较高效,而且,由于字节码并不专对一种特定的机器,因此,Java程序无须重新编译便可在多种不同的计算机上运行。

    先看下java中的编译器和解释器

    Java中引入了虚拟机的概念,即在机器和编译程序之间加入了一层抽象的虚拟机器。这台虚拟的机器在任何平台上都提供给编译程序一个的共同的接口。编译程序只需要面向虚拟机,生成虚拟机能够理解的代码,然后由解释器来将虚拟机代码转换为特定系统的机器码执行。在Java中,这种供虚拟机理解的代码叫做字节码(即扩展为.class的文件),它不面向任何特定的处理器,只面向虚拟机。每一种平台的解释器是不同的,但是实现的虚拟机是相同的。Java源程序经过编译器编译后变成字节码,字节码由虚拟机解释执行,虚拟机将每一条要执行的字节码送给解释器,解释器将其翻译成特定机器上的机器码,然后在特定的机器上运行,这就是上面提到的Java的特点的编译与解释并存的解释。

    Java源代码---->编译器---->jvm可执行的Java字节码(即虚拟指令)---->jvm---->jvm中解释器----->机器可执行的二进制机器码---->程序运行。
    

    什么是Java程序的主类?应用程序和小程序的主类有何不同?

    一个程序中可以有多个类,但只能有一个类是主类。在Java应用程序中,这个主类是指包含main()方法的类。而在Java小程序中,这个主类是一个继承自系统类JApplet或Applet的子类。应用程序的主类不一定要求是public类,但小程序的主类要求必须是public类。主类是Java程序执行的入口点。

    Java应用程序与小程序之间有那些差别?

    简单说应用程序是从主线程启动(也就是main()方法)。applet小程序没有main方法,主要是嵌在浏览器页面上运行(调用init()线程或者run()来启动),嵌入浏览器这点跟flash的小游戏类似。

    Java和C++的区别

    我知道很多人没学过C++,但是面试官就是没事喜欢拿咱们Java和C++比呀!没办法!!!就算没学过C++,也要记下来!

    • 都是面向对象的语言,都支持封装、继承和多态
    • Java不提供指针来直接访问内存,程序内存更加安全
    • Java的类是单继承的,C++支持多重继承;虽然Java的类不可以多继承,但是接口可以多继承。
    • Java有自动内存管理机制,不需要程序员手动释放无用内存

    Oracle JDK 和 OpenJDK 的对比

    1. Oracle JDK版本将每三年发布一次,而OpenJDK版本每三个月发布一次;

    2. OpenJDK 是一个参考模型并且是完全开源的,而Oracle JDK是OpenJDK的一个实现,并不是完全开源的;

    3. Oracle JDK 比 OpenJDK 更稳定。OpenJDK和Oracle JDK的代码几乎相同,但Oracle JDK有更多的类和一些错误修复。因此,如果您想开发企业/商业软件,我建议您选择Oracle JDK,因为它经过了彻底的测试和稳定。某些情况下,有些人提到在使用OpenJDK 可能会遇到了许多应用程序崩溃的问题,但是,只需切换到Oracle JDK就可以解决问题;

    4. 在响应性和JVM性能方面,Oracle JDK与OpenJDK相比提供了更好的性能;

    5. Oracle JDK不会为即将发布的版本提供长期支持,用户每次都必须通过更新到最新版本获得支持来获取最新版本;

    6. Oracle JDK根据二进制代码许可协议获得许可,而OpenJDK根据GPL v2许可获得许可。

    基础语法

    数据类型

    Java有哪些数据类型

    定义:Java语言是强类型语言,对于每一种数据都定义了明确的具体的数据类型,在内存中分配了不同大小的内存空间。

    分类

    • 基本数据类型
      • 数值型
        • 整数类型(byte,short,int,long)
        • 浮点类型(float,double)
      • 字符型(char)
      • 布尔型(boolean)
    • 引用数据类型
      • 类(class)
      • 接口(interface)
      • 数组([])

    Java基本数据类型图

    switch 是否能作用在 byte 上,是否能作用在 long 上,是否能作用在 String 上

    在 Java 5 以前,switch(expr)中,expr 只能是 byte、short、char、int。从 Java5 开始,Java 中引入了枚举类型,expr 也可以是 enum 类型,从 Java 7 开始,expr 还可以是字符串(String),但是长整型(long)在目前所有的版本中都是不可以的。

    用最有效率的方法计算 2 乘以 8

    2 << 3(左移 3 位相当于乘以 2 的 3 次方,右移 3 位相当于除以 2 的 3 次方)。

    Math.round(11.5) 等于多少?Math.round(-11.5)等于多少

    Math.round(11.5)的返回值是 12,Math.round(-11.5)的返回值是-11。四舍五入的原理是在参数上加 0.5 然后进行下取整。

    float f=3.4;是否正确

    不正确。3.4 是双精度数,将双精度型(double)赋值给浮点型(float)属于下转型(down-casting,也称为窄化)会造成精度损失,因此需要强制类型转换float f =(float)3.4; 或者写成 float f =3.4F;。

    short s1 = 1; s1 = s1 + 1;有错吗?short s1 = 1; s1 += 1;有错吗

    对于 short s1 = 1; s1 = s1 + 1;由于 1 是 int 类型,因此 s1+1 运算结果也是 int型,需要强制转换类型才能赋值给 short 型。

    而 short s1 = 1; s1 += 1;可以正确编译,因为 s1+= 1;相当于 s1 = (short(s1 + 1);其中有隐含的强制类型转换。

    编码

    Java语言采用何种编码方案?有何特点?

    Java语言采用Unicode编码标准,Unicode(标准码),它为每个字符制订了一个唯一的数值,因此在任何的语言,平台,程序都可以放心的使用。

    注释

    什么Java注释

    定义:用于解释说明程序的文字

    分类

    • 单行注释
      格式: // 注释文字
    • 多行注释
      格式: /* 注释文字 */
    • 文档注释
      格式:/** 注释文字 */

    作用

    在程序中,尤其是复杂的程序中,适当地加入注释可以增加程序的可读性,有利于程序的修改、调试和交流。注释的内容在程序编译的时候会被忽视,不会产生目标代码,注释的部分不会对程序的执行结果产生任何影响。

    注意事项:多行和文档注释都不能嵌套使用。

    访问修饰符

    访问修饰符 public,private,protected,以及不写(默认)时的区别

    定义:Java中,可以使用访问修饰符来保护对类、变量、方法和构造方法的访问。Java 支持 4 种不同的访问权限。

    分类

    private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
    default (即缺省,什么也不写,不使用任何关键字): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
    protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。
    public : 对所有类可见。使用对象:类、接口、变量、方法

    访问修饰符图

    运算符

    &和&&的区别

    &运算符有两种用法:(1)按位与;(2)逻辑与。

    &&运算符是短路与运算。逻辑与跟短路与的差别是非常巨大的,虽然二者都要求运算符左右两端的布尔值都是true 整个表达式的值才是 true。&&之所以称为短路运算,是因为如果&&左边的表达式的值是 false,右边的表达式会被直接短路掉,不会进行运算。

    注意:逻辑或运算符(|)和短路或运算符(||)的差别也是如此。

    关键字

    Java 有没有 goto

    goto 是 Java 中的保留字,在目前版本的 Java 中没有使用。

    final 有什么用?

    用于修饰类、属性和方法;

    • 被final修饰的类不可以被继承
    • 被final修饰的方法不可以被重写
    • 被final修饰的变量不可以被改变,被final修饰不可变的是变量的引用,而不是引用指向的内容,引用指向的内容是可以改变的

    final finally finalize区别

    • final可以修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、修饰变量表
      示该变量是一个常量不能被重新赋值。
    • finally一般作用在try-catch代码块中,在处理异常的时候,通常我们将一定要执行的代码方法finally代码块
      中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代码。
    • finalize是一个方法,属于Object类的一个方法,而Object类是所有类的父类,该方法一般由垃圾回收器来调
      用,当我们调用System.gc() 方法的时候,由垃圾回收器调用finalize(),回收垃圾,一个对象是否可回收的
      最后判断。

    this关键字的用法

    this是自身的一个对象,代表对象本身,可以理解为:指向对象本身的一个指针。

    this的用法在java中大体可以分为3种:

    1.普通的直接引用,this相当于是指向当前对象本身。

    2.形参与成员名字重名,用this来区分:

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    

    3.引用本类的构造函数

    class Person{
        private String name;
        private int age;
        
        public Person() {
        }
     
        public Person(String name) {
            this.name = name;
        }
        public Person(String name, int age) {
            this(name);
            this.age = age;
        }
    }
    

    super关键字的用法

    super可以理解为是指向自己超(父)类对象的一个指针,而这个超类指的是离自己最近的一个父类。

    super也有三种用法:

    1.普通的直接引用

    与this类似,super相当于是指向当前对象的父类的引用,这样就可以用super.xxx来引用父类的成员。

    2.子类中的成员变量或方法与父类中的成员变量或方法同名时,用super进行区分

    class Person{
        protected String name;
     
        public Person(String name) {
            this.name = name;
        }
     
    }
     
    class Student extends Person{
        private String name;
     
        public Student(String name, String name1) {
            super(name);
            this.name = name1;
        }
     
        public void getInfo(){
            System.out.println(this.name);      //Child
            System.out.println(super.name);     //Father
        }
     
    }
    
    public class Test {
        public static void main(String[] args) {
           Student s1 = new Student("Father","Child");
           s1.getInfo();
     
        }
    }
    

    3.引用父类构造函数

    3、引用父类构造函数

    • super(参数):调用父类中的某一个构造函数(应该为构造函数中的第一条语句)。
    • this(参数):调用本类中另一种形式的构造函数(应该为构造函数中的第一条语句)。

    this与super的区别

    • super: 它引用当前对象的直接父类中的成员(用来访问直接父类中被隐藏的父类中成员数据或函数,基类与派生类中有相同成员定义时如:super.变量名 super.成员函数据名(实参)
    • this:它代表当前对象名(在程序中易产生二义性之处,应使用this来指明当前对象;如果函数的形参与类中的成员数据同名,这时需用this来指明成员变量名)
    • super()和this()类似,区别是,super()在子类中调用父类的构造方法,this()在本类内调用本类的其它构造方法。
    • super()和this()均需放在构造方法内第一行。
    • 尽管可以用this调用一个构造器,但却不能调用两个。
    • this和super不能同时出现在一个构造函数里面,因为this必然会调用其它的构造函数,其它的构造函数必然也会有super语句的存在,所以在同一个构造函数里面有相同的语句,就失去了语句的意义,编译器也不会通过。
    • this()和super()都指的是对象,所以,均不可以在static环境中使用。包括:static变量,static方法,static语句块。
    • 从本质上讲,this是一个指向本对象的指针, 然而super是一个Java关键字。

    static存在的主要意义

    static的主要意义是在于创建独立于具体对象的域变量或者方法。以致于即使没有创建对象,也能使用属性和调用方法

    static关键字还有一个比较关键的作用就是 用来形成静态代码块以优化程序性能。static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。

    为什么说static块可以用来优化程序性能,是因为它的特性:只会在类加载的时候执行一次。因此,很多时候会将一些只需要进行一次的初始化操作都放在static代码块中进行。

    static的独特之处

    1、被static修饰的变量或者方法是独立于该类的任何对象,也就是说,这些变量和方法不属于任何一个实例对象,而是被类的实例对象所共享

    怎么理解 “被类的实例对象所共享” 这句话呢?就是说,一个类的静态成员,它是属于大伙的【大伙指的是这个类的多个对象实例,我们都知道一个类可以创建多个实例!】,所有的类对象共享的,不像成员变量是自个的【自个指的是这个类的单个实例对象】…我觉得我已经讲的很通俗了,你明白了咩?

    2、在该类被第一次加载的时候,就会去加载被static修饰的部分,而且只在类第一次使用时加载并进行初始化,注意这是第一次用就要初始化,后面根据需要是可以再次赋值的。

    3、static变量值在类加载的时候分配空间,以后创建类对象的时候不会重新分配。赋值的话,是可以任意赋值的!

    4、被static修饰的变量或者方法是优先于对象存在的,也就是说当一个类加载完毕之后,即便没有创建对象,也可以去访问。

    static应用场景

    因为static是被类的实例对象所共享,因此如果某个成员变量是被所有对象所共享的,那么这个成员变量就应该定义为静态变量

    因此比较常见的static应用场景有:

    1、修饰成员变量 2、修饰成员方法 3、静态代码块 4、修饰类【只能修饰内部类也就是静态内部类】 5、静态导包

    static注意事项

    1、静态只能访问静态。 2、非静态既可以访问非静态的,也可以访问静态的。

    流程控制语句

    break ,continue ,return 的区别及作用

    break 跳出总上一层循环,不再执行循环(结束当前的循环体)

    continue 跳出本次循环,继续执行下次循环(结束正在执行的循环 进入下一个循环条件)

    return 程序返回,不再执行下面的代码(结束当前的方法 直接返回)

    在 Java 中,如何跳出当前的多重嵌套循环

    在Java中,要想跳出多重循环,可以在外面的循环语句前定义一个标号,然后在里层循环体的代码中使用带有标号的break 语句,即可跳出外层循环。例如:

    public static void main(String[] args) {
        ok:
        for (int i = 0; i < 10; i++) {
            for (int j = 0; j < 10; j++) {
                System.out.println("i=" + i + ",j=" + j);
                if (j == 5) {
                    break ok;
                }
    
            }
        }
    }
    

    面向对象

    面向对象概述

    面向对象和面向过程的区别

    面向过程

    优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;比如单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发,性能是最重要的因素。

    缺点:没有面向对象易维护、易复用、易扩展

    面向对象

    优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护

    缺点:性能比面向过程低

    面向过程是具体化的,流程化的,解决一个问题,你需要一步一步的分析,一步一步的实现。

    面向对象是模型化的,你只需抽象出一个类,这是一个封闭的盒子,在这里你拥有数据也拥有解决问题的方法。需要什么功能直接使用就可以了,不必去一步一步的实现,至于这个功能是如何实现的,管我们什么事?我们会用就可以了。

    面向对象的底层其实还是面向过程,把面向过程抽象成类,然后封装,方便我们使用的就是面向对象了。

    面向对象三大特性

    面向对象的特征有哪些方面

    面向对象的特征主要有以下几个方面

    抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么。

    封装

    封装把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法,如果属性不想被外界访问,我们大可不必提供方法给外界访问。但是如果一个类没有提供给外界访问的方法,那么这个类也没有什么意义了。

    继承

    继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承我们能够非常方便地复用以前的代码。

    关于继承如下 3 点请记住:

    1. 子类拥有父类非 private 的属性和方法。

    2. 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。

    3. 子类可以用自己的方式实现父类的方法。(以后介绍)。

    多态

    所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。

    在Java中有两种形式可以实现多态:继承(多个子类对同一方法的重写)和接口(实现接口并覆盖接口中同一方法)。

    其中Java 面向对象编程三大特性:封装 继承 多态

    封装:隐藏对象的属性和实现细节,仅对外提供公共访问方式,将变化隔离,便于使用,提高复用性和安全性。

    继承:继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承可以提高代码复用性。继承是多态的前提。

    关于继承如下 3 点请记住

    1. 子类拥有父类非 private 的属性和方法。

    2. 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。

    3. 子类可以用自己的方式实现父类的方法。

    多态性:父类或接口定义的引用变量可以指向子类或具体实现类的实例对象。提高了程序的拓展性。

    在Java中有两种形式可以实现多态:继承(多个子类对同一方法的重写)和接口(实现接口并覆盖接口中同一方法)。

    方法重载(overload)实现的是编译时的多态性(也称为前绑定),而方法重写(override)实现的是运行时的多态性(也称为后绑定)。

    一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。运行时的多态是面向对象最精髓的东西,要实现多态需要做两件事:

    • 方法重写(子类继承父类并重写父类中已有的或抽象的方法);
    • 对象造型(用父类型引用子类型对象,这样同样的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为)。

    什么是多态机制?Java语言是如何实现多态的?

    所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。

    多态分为编译时多态和运行时多态。其中编辑时多态是静态的,主要是指方法的重载,它是根据参数列表的不同来区分不同的函数,通过编辑之后会变成两个不同的函数,在运行时谈不上多态。而运行时多态是动态的,它是通过动态绑定来实现的,也就是我们所说的多态性。

    多态的实现

    Java实现多态有三个必要条件:继承、重写、向上转型。

    继承:在多态中必须存在有继承关系的子类和父类。

    重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。

    向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才能够具备技能调用父类的方法和子类的方法。

    只有满足了上述三个条件,我们才能够在同一个继承结构中使用统一的逻辑实现代码处理不同的对象,从而达到执行不同的行为。

    对于Java而言,它多态的实现机制遵循一个原则:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。

    面向对象五大基本原则是什么(可选)

    • 单一职责原则SRP(Single Responsibility Principle)
      类的功能要单一,不能包罗万象,跟杂货铺似的。
    • 开放封闭原则OCP(Open-Close Principle)
      一个模块对于拓展是开放的,对于修改是封闭的,想要增加功能热烈欢迎,想要修改,哼,一万个不乐意。
    • 里式替换原则LSP(the Liskov Substitution Principle LSP)
      子类可以替换父类出现在父类能够出现的任何地方。比如你能代表你爸去你姥姥家干活。哈哈~~
    • 依赖倒置原则DIP(the Dependency Inversion Principle DIP)
      高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象。抽象不应该依赖于具体实现,具体实现应该依赖于抽象。就是你出国要说你是中国人,而不能说你是哪个村子的。比如说中国人是抽象的,下面有具体的xx省,xx市,xx县。你要依赖的抽象是中国人,而不是你是xx村的。
    • 接口分离原则ISP(the Interface Segregation Principle ISP)
      设计时采用多个与特定客户类有关的接口比采用一个通用的接口要好。就比如一个手机拥有打电话,看视频,玩游戏等功能,把这几个功能拆分成不同的接口,比在一个接口里要好的多。

    类与接口

    抽象类和接口的对比

    抽象类是用来捕捉子类的通用特性的。接口是抽象方法的集合。

    从设计层面来说,抽象类是对类的抽象,是一种模板设计,接口是行为的抽象,是一种行为的规范。

    相同点

    • 接口和抽象类都不能实例化
    • 都位于继承的顶端,用于被其他实现或继承
    • 都包含抽象方法,其子类都必须覆写这些抽象方法

    不同点

    参数抽象类接口
    声明抽象类使用abstract关键字声明接口使用interface关键字声明
    实现子类使用extends关键字来继承抽象类。如果子类不是抽象类的话,它需要提供抽象类中所有声明的方法的实现子类使用implements关键字来实现接口。它需要提供接口中所有声明的方法的实现
    构造器抽象类可以有构造器接口不能有构造器
    访问修饰符抽象类中的方法可以是任意访问修饰符接口方法默认修饰符是public。并且不允许定义为 private 或者 protected
    多继承一个类最多只能继承一个抽象类一个类可以实现多个接口
    字段声明抽象类的字段声明可以是任意的接口的字段默认都是 static 和 final 的

    备注:Java8中接口中引入默认方法和静态方法,以此来减少抽象类和接口之间的差异。

    现在,我们可以为接口提供默认实现的方法了,并且不用强制子类来实现它。

    接口和抽象类各有优缺点,在接口和抽象类的选择上,必须遵守这样一个原则:

    • 行为模型应该总是通过接口而不是抽象类定义,所以通常是优先选用接口,尽量少用抽象类。
    • 选择抽象类的时候通常是如下情况:需要定义子类的行为,又要为子类提供通用的功能。

    普通类和抽象类有哪些区别?

    • 普通类不能包含抽象方法,抽象类可以包含抽象方法。
    • 抽象类不能直接实例化,普通类可以直接实例化。

    抽象类能使用 final 修饰吗?

    不能,定义抽象类就是让其他类继承的,如果定义为 final 该类就不能被继承,这样彼此就会产生矛盾,所以 final 不能修饰抽象类

    创建一个对象用什么关键字?对象实例与对象引用有何不同?

    new关键字,new创建对象实例(对象实例在堆内存中),对象引用指向对象实例(对象引用存放在栈内存中)。一个对象引用可以指向0个或1个对象(一根绳子可以不系气球,也可以系一个气球);一个对象可以有n个引用指向它(可以用n条绳子系住一个气球)

    变量与方法

    成员变量与局部变量的区别有哪些

    变量:在程序执行的过程中,在某个范围内其值可以发生改变的量。从本质上讲,变量其实是内存中的一小块区域

    成员变量:方法外部,类内部定义的变量

    局部变量:类的方法中的变量。

    成员变量和局部变量的区别

    作用域

    成员变量:针对整个类有效。
    局部变量:只在某个范围内有效。(一般指的就是方法,语句体内)

    存储位置

    成员变量:随着对象的创建而存在,随着对象的消失而消失,存储在堆内存中。
    局部变量:在方法被调用,或者语句被执行的时候存在,存储在栈内存中。当方法调用完,或者语句结束后,就自动释放。

    生命周期

    成员变量:随着对象的创建而存在,随着对象的消失而消失
    局部变量:当方法调用完,或者语句结束后,就自动释放。

    初始值

    成员变量:有默认初始值。

    局部变量:没有默认初始值,使用前必须赋值。

    使用原则

    在使用变量时需要遵循的原则为:就近原则
    首先在局部范围找,有就使用;接着在成员位置找。

    在Java中定义一个不做事且没有参数的构造方法的作用

    Java程序在执行子类的构造方法之前,如果没有用super()来调用父类特定的构造方法,则会调用父类中“没有参数的构造方法”。因此,如果父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用super()来调用父类中特定的构造方法,则编译时将发生错误,因为Java程序在父类中找不到没有参数的构造方法可供执行。解决办法是在父类里加上一个不做事且没有参数的构造方法。

    在调用子类构造方法之前会先调用父类没有参数的构造方法,其目的是?

    帮助子类做初始化工作。

    一个类的构造方法的作用是什么?若一个类没有声明构造方法,改程序能正确执行吗?为什么?

    主要作用是完成对类对象的初始化工作。可以执行。因为一个类即使没有声明构造方法也会有默认的不带参数的构造方法。

    构造方法有哪些特性?

    名字与类名相同;

    没有返回值,但不能用void声明构造函数;

    生成类的对象时自动执行,无需调用。

    静态变量和实例变量区别

    静态变量: 静态变量由于不属于任何实例对象,属于类的,所以在内存中只会有一份,在类的加载过程中,JVM只为静态变量分配一次内存空间。

    实例变量: 每次创建对象,都会为每个对象分配成员变量内存空间,实例变量是属于实例对象的,在内存中,创建几次对象,就有几份成员变量。

    静态变量与普通变量区别

    static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。

    还有一点就是static成员变量的初始化顺序按照定义的顺序进行初始化。

    静态方法和实例方法有何不同?

    静态方法和实例方法的区别主要体现在两个方面:

    1. 在外部调用静态方法时,可以使用"类名.方法名"的方式,也可以使用"对象名.方法名"的方式。而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象。
    2. 静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),而不允许访问实例成员变量和实例方法;实例方法则无此限制

    在一个静态方法内调用一个非静态成员为什么是非法的?

    由于静态方法可以不通过对象进行调用,因此在静态方法里,不能调用其他非静态变量,也不可以访问非静态变量成员。

    什么是方法的返回值?返回值的作用是什么?

    方法的返回值是指我们获取到的某个方法体中的代码执行后产生的结果!(前提是该方法可能产生结果)。返回值的作用:接收出结果,使得它可以用于其他的操作!

    内部类

    什么是内部类?

    在Java中,可以将一个类的定义放在另外一个类的定义内部,这就是内部类。内部类本身就是类的一个属性,与其他属性定义方式一致。

    内部类的分类有哪些

    内部类可以分为四种:成员内部类、局部内部类、匿名内部类和静态内部类

    静态内部类

    定义在类内部的静态类,就是静态内部类。

    public class Outer {
    
        private static int radius = 1;
    
        static class StaticInner {
            public void visit() {
                System.out.println("visit outer static  variable:" + radius);
            }
        }
    }
    

    静态内部类可以访问外部类所有的静态变量,而不可访问外部类的非静态变量;静态内部类的创建方式,new 外部类.静态内部类(),如下:

    Outer.StaticInner inner = new Outer.StaticInner();
    inner.visit();
    
    成员内部类

    定义在类内部,成员位置上的非静态类,就是成员内部类。

    public class Outer {
    
        private static  int radius = 1;
        private int count =2;
        
         class Inner {
            public void visit() {
                System.out.println("visit outer static  variable:" + radius);
                System.out.println("visit outer   variable:" + count);
            }
        }
    }
    

    成员内部类可以访问外部类所有的变量和方法,包括静态和非静态,私有和公有。成员内部类依赖于外部类的实例,它的创建方式外部类实例.new 内部类(),如下:

    Outer outer = new Outer();
    Outer.Inner inner = outer.new Inner();
    inner.visit();
    
    局部内部类

    定义在方法中的内部类,就是局部内部类。

    public class Outer {
    
        private  int out_a = 1;
        private static int STATIC_b = 2;
    
        public void testFunctionClass(){
            int inner_c =3;
            class Inner {
                private void fun(){
                    System.out.println(out_a);
                    System.out.println(STATIC_b);
                    System.out.println(inner_c);
                }
            }
            Inner  inner = new Inner();
            inner.fun();
        }
        public static void testStaticFunctionClass(){
            int d =3;
            class Inner {
                private void fun(){
                    // System.out.println(out_a); 编译错误,定义在静态方法中的局部类不可以访问外部类的实例变量
                    System.out.println(STATIC_b);
                    System.out.println(d);
                }
            }
            Inner  inner = new Inner();
            inner.fun();
        }
    }
    

    定义在实例方法中的局部类可以访问外部类的所有变量和方法,定义在静态方法中的局部类只能访问外部类的静态变量和方法。局部内部类的创建方式,在对应方法内,new 内部类(),如下:

     public static void testStaticFunctionClass(){
        class Inner {
        }
        Inner  inner = new Inner();
     }
    
    匿名内部类

    匿名内部类就是没有名字的内部类,日常开发中使用的比较多。

    public class Outer {
    
        private void test(final int i) {
            new Service() {
                public void method() {
                    for (int j = 0; j < i; j++) {
                        System.out.println("匿名内部类" );
                    }
                }
            }.method();
        }
     }
     //匿名内部类必须继承或实现一个已有的接口 
     interface Service{
        void method();
    }
    

    除了没有名字,匿名内部类还有以下特点:

    • 匿名内部类必须继承一个抽象类或者实现一个接口。
    • 匿名内部类不能定义任何静态成员和静态方法。
    • 当所在的方法的形参需要被匿名内部类使用时,必须声明为 final。
    • 匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。

    匿名内部类创建方式:

    new/接口{ 
      //匿名内部类实现部分
    }
    

    内部类的优点

    我们为什么要使用内部类呢?因为它有以下优点:

    • 一个内部类对象可以访问创建它的外部类对象的内容,包括私有数据!
    • 内部类不为同一包的其他类所见,具有很好的封装性;
    • 内部类有效实现了“多重继承”,优化 java 单继承的缺陷。
    • 匿名内部类可以很方便的定义回调。

    内部类有哪些应用场景

    1. 一些多算法场合
    2. 解决一些非面向对象的语句块。
    3. 适当使用内部类,使得代码更加灵活和富有扩展性。
    4. 当某个类除了它的外部类,不再被其他的类使用时。

    局部内部类和匿名内部类访问局部变量的时候,为什么变量必须要加上final?

    局部内部类和匿名内部类访问局部变量的时候,为什么变量必须要加上final呢?它内部原理是什么呢?

    先看这段代码:

    public class Outer {
    
        void outMethod(){
            final int a =10;
            class Inner {
                void innerMethod(){
                    System.out.println(a);
                }
    
            }
        }
    }
    

    以上例子,为什么要加final呢?是因为生命周期不一致, 局部变量直接存储在栈中,当方法执行结束后,非final的局部变量就被销毁。而局部内部类对局部变量的引用依然存在,如果局部内部类要调用局部变量时,就会出错。加了final,可以确保局部内部类使用的变量与外层的局部变量区分开,解决了这个问题。

    内部类相关,看程序说出运行结果

    public class Outer {
        private int age = 12;
    
        class Inner {
            private int age = 13;
            public void print() {
                int age = 14;
                System.out.println("局部变量:" + age);
                System.out.println("内部类变量:" + this.age);
                System.out.println("外部类变量:" + Outer.this.age);
            }
        }
    
        public static void main(String[] args) {
            Outer.Inner in = new Outer().new Inner();
            in.print();
        }
    
    }
    

    运行结果:

    局部变量:14
    内部类变量:13
    外部类变量:12
    

    重写与重载

    构造器(constructor)是否可被重写(override)

    构造器不能被继承,因此不能被重写,但可以被重载。

    重载(Overload)和重写(Override)的区别。重载的方法能否根据返回类型进行区分?

    方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。

    重载:发生在同一个类中,方法名相同参数列表不同(参数类型不同、个数不同、顺序不同),与方法返回值和访问修饰符无关,即重载的方法不能根据返回类型进行区分

    重写:发生在父子类中,方法名、参数列表必须相同,返回值小于等于父类,抛出的异常小于等于父类,访问修饰符大于等于父类(里氏代换原则);如果父类方法访问修饰符为private则子类中就不是重写。

    对象相等判断

    == 和 equals 的区别是什么

    == : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同一个对象。(基本数据类型 == 比较的是值,引用数据类型 == 比较的是内存地址)

    equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况:

    情况1:类没有覆盖 equals() 方法。则通过 equals() 比较该类的两个对象时,等价于通过“==”比较这两个对象。

    情况2:类覆盖了 equals() 方法。一般,我们都覆盖 equals() 方法来两个对象的内容相等;若它们的内容相等,则返回 true (即,认为这两个对象相等)。

    举个例子:

    public class test1 {
        public static void main(String[] args) {
            String a = new String("ab"); // a 为一个引用
            String b = new String("ab"); // b为另一个引用,对象的内容一样
            String aa = "ab"; // 放在常量池中
            String bb = "ab"; // 从常量池中查找
            if (aa == bb) // true
                System.out.println("aa==bb");
            if (a == b) // false,非同一对象
                System.out.println("a==b");
            if (a.equals(b)) // true
                System.out.println("aEQb");
            if (42 == 42.0) { // true
                System.out.println("true");
            }
        }
    }
    

    说明:

    • String中的equals方法是被重写过的,因为object的equals方法是比较的对象的内存地址,而String的equals方法比较的是对象的值。
    • 当创建String类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个String对象。

    hashCode 与 equals (重要)

    HashSet如何检查重复

    两个对象的 hashCode() 相同,则 equals() 也一定为 true,对吗?

    hashCode和equals方法的关系

    面试官可能会问你:“你重写过 hashcode 和 equals 么,为什么重写equals时必须重写hashCode方法?”

    hashCode()介绍

    hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode() 定义在JDK的Object.java中,这就意味着Java中的任何类都包含有hashCode()函数。

    散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码!(可以快速找到所需要的对象)

    为什么要有 hashCode

    我们以“HashSet 如何检查重复”为例子来说明为什么要有 hashCode

    当你把对象加入 HashSet 时,HashSet 会先计算对象的 hashcode 值来判断对象加入的位置,同时也会与其他已经加入的对象的 hashcode 值作比较,如果没有相符的hashcode,HashSet会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调用 equals()方法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。(摘自我的Java启蒙书《Head first java》第二版)。这样我们就大大减少了 equals 的次数,相应就大大提高了执行速度。

    hashCode()与equals()的相关规定

    如果两个对象相等,则hashcode一定也是相同的

    两个对象相等,对两个对象分别调用equals方法都返回true

    两个对象有相同的hashcode值,它们也不一定是相等的

    因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖

    hashCode() 的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 class 的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)

    对象的相等与指向他们的引用相等,两者有什么不同?

    对象的相等 比的是内存中存放的内容是否相等而 引用相等 比较的是他们指向的内存地址是否相等。

    值传递

    当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递

    是值传递。Java 语言的方法调用只支持参数的值传递。当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的属性可以在被调用过程中被改变,但对对象引用的改变是不会影响到调用者的

    为什么 Java 中只有值传递

    首先回顾一下在程序设计语言中有关将参数传递给方法(或函数)的一些专业术语。按值调用(call by value)表示方法接收的是调用者提供的值,而按引用调用(call by reference)表示方法接收的是调用者提供的变量地址。一个方法可以修改传递引用所对应的变量值,而不能修改传递值调用所对应的变量值。 它用来描述各种程序设计语言(不只是Java)中方法参数传递方式。

    Java程序设计语言总是采用按值调用。也就是说,方法得到的是所有参数值的一个拷贝,也就是说,方法不能修改传递给它的任何参数变量的内容。

    下面通过 3 个例子来给大家说明

    example 1

    public static void main(String[] args) {
        int num1 = 10;
        int num2 = 20;
    
        swap(num1, num2);
    
        System.out.println("num1 = " + num1);
        System.out.println("num2 = " + num2);
    }
    
    public static void swap(int a, int b) {
        int temp = a;
        a = b;
        b = temp;
    
        System.out.println("a = " + a);
        System.out.println("b = " + b);
    }
    

    结果

    a = 20
    b = 10
    num1 = 10
    num2 = 20
    

    解析

    img

    在swap方法中,a、b的值进行交换,并不会影响到 num1、num2。因为,a、b中的值,只是从 num1、num2 的复制过来的。也就是说,a、b相当于num1、num2 的副本,副本的内容无论怎么修改,都不会影响到原件本身。

    通过上面例子,我们已经知道了一个方法不能修改一个基本数据类型的参数,而对象引用作为参数就不一样,请看 example2.

    example 2

        public static void main(String[] args) {
            int[] arr = { 1, 2, 3, 4, 5 };
            System.out.println(arr[0]);
            change(arr);
            System.out.println(arr[0]);
        }
    
        public static void change(int[] array) {
            // 将数组的第一个元素变为0
            array[0] = 0;
        }
    

    结果

    1
    0
    

    解析

    img

    array 被初始化 arr 的拷贝也就是一个对象的引用,也就是说 array 和 arr 指向的时同一个数组对象。 因此,外部对引用对象的改变会反映到所对应的对象上。

    通过 example2 我们已经看到,实现一个改变对象参数状态的方法并不是一件难事。理由很简单,方法得到的是对象引用的拷贝,对象引用及其他的拷贝同时引用同一个对象。

    很多程序设计语言(特别是,C++和Pascal)提供了两种参数传递的方式:值调用和引用调用。有些程序员(甚至本书的作者)认为Java程序设计语言对对象采用的是引用调用,实际上,这种理解是不对的。由于这种误解具有一定的普遍性,所以下面给出一个反例来详细地阐述一下这个问题。

    example 3

    public class Test {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            Student s1 = new Student("小张");
            Student s2 = new Student("小李");
            Test.swap(s1, s2);
            System.out.println("s1:" + s1.getName());
            System.out.println("s2:" + s2.getName());
        }
    
        public static void swap(Student x, Student y) {
            Student temp = x;
            x = y;
            y = temp;
            System.out.println("x:" + x.getName());
            System.out.println("y:" + y.getName());
        }
    }
    

    结果

    x:小李
    y:小张
    s1:小张
    s2:小李
    

    解析

    交换之前:

    img

    交换之后:

    img

    通过上面两张图可以很清晰的看出: 方法并没有改变存储在变量 s1 和 s2 中的对象引用。swap方法的参数x和y被初始化为两个对象引用的拷贝,这个方法交换的是这两个拷贝

    总结

    Java程序设计语言对对象采用的不是引用调用,实际上,对象引用是按值传递的。

    下面再总结一下Java中方法参数的使用情况:

    • 一个方法不能修改一个基本数据类型的参数(即数值型或布尔型》
    • 一个方法可以改变一个对象参数的状态。
    • 一个方法不能让对象参数引用一个新的对象。

    值传递和引用传递有什么区别

    值传递:指的是在方法调用时,传递的参数是按值的拷贝传递,传递的是值的拷贝,也就是说传递后就互不相关了。

    引用传递:指的是在方法调用时,传递的参数是按引用进行传递,其实传递的引用的地址,也就是变量所对应的内存空间的地址。传递的是值的引用,也就是说传递前和传递后都指向同一个引用(也就是同一个内存空间)。

    Java包

    JDK 中常用的包有哪些

    • java.lang:这个是系统的基础类;
    • java.io:这里面是所有输入输出有关的类,比如文件操作等;
    • java.nio:为了完善 io 包中的功能,提高 io 包中性能而写的一个新包;
    • java.net:这里面是与网络有关的类;
    • java.util:这个是系统辅助类,特别是集合类;
    • java.sql:这个是数据库操作的类。

    import java和javax有什么区别

    刚开始的时候 JavaAPI 所必需的包是 java 开头的包,javax 当时只是扩展 API 包来说使用。然而随着时间的推移,javax 逐渐的扩展成为 Java API 的组成部分。但是,将扩展从 javax 包移动到 java 包将是太麻烦了,最终会破坏一堆现有的代码。因此,最终决定 javax 包将成为标准API的一部分。

    所以,实际上java和javax没有区别。这都是一个名字。

    IO流

    java 中 IO 流分为几种?

    • 按照流的流向分,可以分为输入流和输出流;
    • 按照操作单元划分,可以划分为字节流和字符流;
    • 按照流的角色划分为节点流和处理流。

    Java Io流共涉及40多个类,这些类看上去很杂乱,但实际上很有规则,而且彼此之间存在非常紧密的联系, Java I0流的40多个类都是从如下4个抽象类基类中派生出来的。

    • InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
    • OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。

    按操作方式分类结构图:

    IO-操作方式分类

    按操作对象分类结构图:

    IO-操作对象分类

    BIO,NIO,AIO 有什么区别?

    简答

    • BIO:Block IO 同步阻塞式 IO,就是我们平常使用的传统 IO,它的特点是模式简单使用方便,并发处理能力低。
    • NIO:Non IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。
    • AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,异步 IO 的操作基于事件和回调机制。

    详细回答

    • BIO (Blocking I/O): 同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。在活动连接数不是特别高(小于单机1000)的情况下,这种模型是比较不错的,可以让每一个连接专注于自己的 I/O 并且编程模型简单,也不用过多考虑系统的过载、限流等问题。线程池本身就是一个天然的漏斗,可以缓冲一些系统处理不了的连接或请求。但是,当面对十万甚至百万级连接的时候,传统的 BIO 模型是无能为力的。因此,我们需要一种更高效的 I/O 处理模型来应对更高的并发量。
    • NIO (New I/O): NIO是一种同步非阻塞的I/O模型,在Java 1.4 中引入了NIO框架,对应 java.nio 包,提供了 Channel , Selector,Buffer等抽象。NIO中的N可以理解为Non-blocking,不单纯是New。它支持面向缓冲的,基于通道的I/O操作方法。 NIO提供了与传统BIO模型中的 SocketServerSocket 相对应的 SocketChannelServerSocketChannel 两种不同的套接字通道实现,两种通道都支持阻塞和非阻塞两种模式。阻塞模式使用就像传统中的支持一样,比较简单,但是性能和可靠性都不好;非阻塞模式正好与之相反。对于低负载、低并发的应用程序,可以使用同步阻塞I/O来提升开发速率和更好的维护性;对于高负载、高并发的(网络)应用,应使用 NIO 的非阻塞模式来开发
    • AIO (Asynchronous I/O): AIO 也就是 NIO 2。在 Java 7 中引入了 NIO 的改进版 NIO 2,它是异步非阻塞的IO模型。异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。AIO 是异步IO的缩写,虽然 NIO 在网络操作中,提供了非阻塞的方法,但是 NIO 的 IO 行为还是同步的。对于 NIO 来说,我们的业务线程是在 IO 操作准备好时,得到通知,接着就由这个线程自行进行 IO 操作,IO操作本身是同步的。查阅网上相关资料,我发现就目前来说 AIO 的应用还不是很广泛,Netty 之前也尝试使用过 AIO,不过又放弃了。

    Files的常用方法都有哪些?

    • Files. exists():检测文件路径是否存在。
    • Files. createFile():创建文件。
    • Files. createDirectory():创建文件夹。
    • Files. delete():删除一个文件或目录。
    • Files. copy():复制文件。
    • Files. move():移动文件。
    • Files. size():查看文件个数。
    • Files. read():读取文件。
    • Files. write():写入文件。

    反射

    什么是反射机制?

    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

    静态编译和动态编译

    • **静态编译:**在编译时确定类型,绑定对象
    • **动态编译:**运行时确定类型,绑定对象

    反射机制优缺点

    • 优点: 运行期类型的判断,动态加载类,提高代码灵活度。
    • 缺点: 性能瓶颈:反射相当于一系列解释操作,通知 JVM 要做的事情,性能比直接的java代码要慢很多。

    反射机制的应用场景有哪些?

    反射是框架设计的灵魂。

    在我们平时的项目开发过程中,基本上很少会直接使用到反射机制,但这不能说明反射机制没有用,实际上有很多设计、开发都与反射机制有关,例如模块化的开发,通过反射去调用对应的字节码;动态代理设计模式也采用了反射机制,还有我们日常使用的 Spring/Hibernate 等框架也大量使用到了反射机制。

    举例:①我们在使用JDBC连接数据库时使用Class.forName()通过反射加载数据库的驱动程序;②Spring框架也用到很多反射机制,最经典的就是xml的配置模式。Spring 通过 XML 配置模式装载 Bean 的过程:1) 将程序内所有 XML 或 Properties 配置文件加载入内存中; 2)Java类里面解析xml或properties里面的内容,得到对应实体类的字节码字符串以及相关的属性信息; 3)使用反射机制,根据这个字符串获得某个类的Class实例; 4)动态配置实例的属性

    Java获取反射的三种方法

    1.通过new对象实现反射机制 2.通过路径实现反射机制 3.通过类名实现反射机制

    public class Student {
        private int id;
        String name;
        protected boolean sex;
        public float score;
    }
    
    public class Get {
        //获取反射机制三种方式
        public static void main(String[] args) throws ClassNotFoundException {
            //方式一(通过建立对象)
            Student stu = new Student();
            Class classobj1 = stu.getClass();
            System.out.println(classobj1.getName());
            //方式二(所在通过路径-相对路径)
            Class classobj2 = Class.forName("fanshe.Student");
            System.out.println(classobj2.getName());
            //方式三(通过类名)
            Class classobj3 = Student.class;
            System.out.println(classobj3.getName());
        }
    }
    

    网络编程

    网络编程的面试题可以查看我的这篇文章重学TCP/IP协议和三次握手四次挥手,内容不仅包括TCP/IP协议和三次握手四次挥手的知识,还包括计算机网络体系结构,HTTP协议,get请求和post请求区别,session和cookie的区别等,欢迎大家阅读。

    常用API

    String相关

    字符型常量和字符串常量的区别

    1. 形式上: 字符常量是单引号引起的一个字符 字符串常量是双引号引起的若干个字符
    2. 含义上: 字符常量相当于一个整形值(ASCII值),可以参加表达式运算 字符串常量代表一个地址值(该字符串在内存中存放位置)
    3. 占内存大小 字符常量只占两个字节 字符串常量占若干个字节(至少一个字符结束标志)

    什么是字符串常量池?

    字符串常量池位于堆内存中,专门用来存储字符串常量,可以提高内存的使用率,避免开辟多块空间存储相同的字符串,在创建字符串时 JVM 会首先检查字符串常量池,如果该字符串已经存在池中,则返回它的引用,如果不存在,则实例化一个字符串放到池中,并返回其引用。

    String 是最基本的数据类型吗

    不是。Java 中的基本数据类型只有 8 个 :byte、short、int、long、float、double、char、boolean;除了基本类型(primitive type),剩下的都是引用类型(referencetype),Java 5 以后引入的枚举类型也算是一种比较特殊的引用类型。

    这是很基础的东西,但是很多初学者却容易忽视,Java 的 8 种基本数据类型中不包括 String,基本数据类型中用来描述文本数据的是 char,但是它只能表示单个字符,比如 ‘a’,‘好’ 之类的,如果要描述一段文本,就需要用多个 char 类型的变量,也就是一个 char 类型数组,比如“你好” 就是长度为2的数组 char[] chars = {‘你’,‘好’};

    但是使用数组过于麻烦,所以就有了 String,String 底层就是一个 char 类型的数组,只是使用的时候开发者不需要直接操作底层数组,用更加简便的方式即可完成对字符串的使用。

    String有哪些特性

    • 不变性:String 是只读字符串,是一个典型的 immutable 对象,对它进行任何操作,其实都是创建一个新的对象,再把引用指向该对象。不变模式的主要作用在于当一个对象需要被多线程共享并频繁访问时,可以保证数据的一致性。

    • 常量池优化:String 对象创建之后,会在字符串常量池中进行缓存,如果下次创建同样的对象时,会直接返回缓存的引用。

    • final:使用 final 来定义 String 类,表示 String 类不能被继承,提高了系统的安全性。

    String为什么是不可变的吗?

    简单来说就是String类利用了final修饰的char类型数组存储字符,源码如下图所以:

    /** The value is used for character storage. */
    private final char value[];
    

    String真的是不可变的吗?

    我觉得如果别人问这个问题的话,回答不可变就可以了。 下面只是给大家看两个有代表性的例子:

    1) String不可变但不代表引用不可以变

    String str = "Hello";
    str = str + " World";
    System.out.println("str=" + str);
    

    结果:

    str=Hello World
    

    解析:

    实际上,原来String的内容是不变的,只是str由原来指向"Hello"的内存地址转为指向"Hello World"的内存地址而已,也就是说多开辟了一块内存区域给"Hello World"字符串。

    2) 通过反射是可以修改所谓的“不可变”对象

    // 创建字符串"Hello World", 并赋给引用s
    String s = "Hello World";
    
    System.out.println("s = " + s); // Hello World
    
    // 获取String类中的value字段
    Field valueFieldOfString = String.class.getDeclaredField("value");
    
    // 改变value属性的访问权限
    valueFieldOfString.setAccessible(true);
    
    // 获取s对象上的value属性的值
    char[] value = (char[]) valueFieldOfString.get(s);
    
    // 改变value所引用的数组中的第5个字符
    value[5] = '_';
    
    System.out.println("s = " + s); // Hello_World
    

    结果:

    s = Hello World
    s = Hello_World
    

    解析:

    用反射可以访问私有成员, 然后反射出String对象中的value属性, 进而改变通过获得的value引用改变数组的结构。但是一般我们不会这么做,这里只是简单提一下有这个东西。

    是否可以继承 String 类

    String 类是 final 类,不可以被继承。

    String str="i"与 String str=new String(“i”)一样吗?

    不一样,因为内存的分配方式不一样。String str="i"的方式,java 虚拟机会将其分配到常量池中;而 String str=new String(“i”) 则会被分到堆内存中。

    String s = new String(“xyz”);创建了几个字符串对象

    两个对象,一个是静态区的"xyz",一个是用new创建在堆上的对象。

    String str1 = "hello"; //str1指向静态区
    String str2 = new String("hello");  //str2指向堆上的对象
    String str3 = "hello";
    String str4 = new String("hello");
    System.out.println(str1.equals(str2)); //true
    System.out.println(str2.equals(str4)); //true
    System.out.println(str1 == str3); //true
    System.out.println(str1 == str2); //false
    System.out.println(str2 == str4); //false
    System.out.println(str2 == "hello"); //false
    str2 = str1;
    System.out.println(str2 == "hello"); //true
    

    如何将字符串反转?

    使用 StringBuilder 或者 stringBuffer 的 reverse() 方法。

    示例代码:

    // StringBuffer reverse
    StringBuffer stringBuffer = new StringBuffer();
    stringBuffer. append("abcdefg");
    System. out. println(stringBuffer. reverse()); // gfedcba
    // StringBuilder reverse
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder. append("abcdefg");
    System. out. println(stringBuilder. reverse()); // gfedcba
    

    数组有没有 length()方法?String 有没有 length()方法

    数组没有 length()方法 ,有 length 的属性。String 有 length()方法。JavaScript中,获得字符串的长度是通过 length 属性得到的,这一点容易和 Java 混淆。

    String 类的常用方法都有那些?

    • indexOf():返回指定字符的索引。
    • charAt():返回指定索引处的字符。
    • replace():字符串替换。
    • trim():去除字符串两端空白。
    • split():分割字符串,返回一个分割后的字符串数组。
    • getBytes():返回字符串的 byte 类型数组。
    • length():返回字符串长度。
    • toLowerCase():将字符串转成小写字母。
    • toUpperCase():将字符串转成大写字符。
    • substring():截取字符串。
    • equals():字符串比较。

    在使用 HashMap 的时候,用 String 做 key 有什么好处?

    HashMap 内部实现是通过 key 的 hashcode 来确定 value 的存储位置,因为字符串是不可变的,所以当创建字符串时,它的 hashcode 被缓存下来,不需要再次计算,所以相比于其他对象更快。

    String和StringBuffer、StringBuilder的区别是什么?String为什么是不可变的

    可变性

    String类中使用字符数组保存字符串,private final char value[],所以string对象是不可变的。StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,char[] value,这两种对象都是可变的。

    线程安全性

    String中的对象是不可变的,也就可以理解为常量,线程安全。AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。

    性能

    每次对String 类型进行改变的时候,都会生成一个新的String对象,然后将指针指向新的String 对象。StringBuffer每次都会对StringBuffer对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用StirngBuilder 相比使用StringBuffer 仅能获得10%~15% 左右的性能提升,但却要冒多线程不安全的风险。

    对于三者使用的总结

    如果要操作少量的数据用 = String

    单线程操作字符串缓冲区 下操作大量数据 = StringBuilder

    多线程操作字符串缓冲区 下操作大量数据 = StringBuffer

    Date相关

    包装类相关

    自动装箱与拆箱

    装箱:将基本类型用它们对应的引用类型包装起来;

    拆箱:将包装类型转换为基本数据类型;

    int 和 Integer 有什么区别

    Java 是一个近乎纯洁的面向对象编程语言,但是为了编程的方便还是引入了基本数据类型,但是为了能够将这些基本数据类型当成对象操作,Java 为每一个基本数据类型都引入了对应的包装类型(wrapper class),int 的包装类就是 Integer,从 Java 5 开始引入了自动装箱/拆箱机制,使得二者可以相互转换。

    Java 为每个原始类型提供了包装类型:

    原始类型: boolean,char,byte,short,int,long,float,double

    包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double

    Integer a= 127 与 Integer b = 127相等吗

    对于对象引用类型:==比较的是对象的内存地址。
    对于基本数据类型:==比较的是值。

    如果整型字面量的值在-128到127之间,那么自动装箱时不会new新的Integer对象,而是直接引用常量池中的Integer对象,超过范围 a1==b1的结果是false

    public static void main(String[] args) {
        Integer a = new Integer(3);
        Integer b = 3;  // 将3自动装箱成Integer类型
        int c = 3;
        System.out.println(a == b); // false 两个引用没有引用同一对象
        System.out.println(a == c); // true a自动拆箱成int类型再和c比较
        System.out.println(b == c); // true
    
        Integer a1 = 128;
        Integer b1 = 128;
        System.out.println(a1 == b1); // false
    
        Integer a2 = 127;
        Integer b2 = 127;
        System.out.println(a2 == b2); // true
    }
    

    常用工具类库

    单元测试

    日志

    展开全文
  • 内容索引:JAVA源码,媒体网络,飞鸽传书 Java局域网通信——飞鸽传书源代码,大家都知道VB版、VC版还有Delphi版的飞鸽传书软件,但是Java版的确实不多,因此这个Java文件传输实例不可错过,Java网络编程技能的提升很有...
  • 数据库相关知识 1. 数据库的基本概念 2. MySQL数据库软件 3. SQL 数据库的基本概念 数据库的英文单词 : DataBase 简称 : DB 什么是数据库? 用于存储和管理数据的仓库 数据库的特点 持久化存储数据的,其实...

    数据库相关知识

    数据库的基本概念

    1. 数据库的英文单词 : DataBase 简称 : DB

    2. 什么是数据库?

      1. 用于存储和管理数据的仓库
    3. 数据库的特点

      1. 持久化存储数据的,其实数据库就是一个文件管理系统
      2. 方便存储和管理数据
      3. 使用了统一的方式操作数据库 – SQL
    4. 常见的数据库软件

      1. MySQL : 开源免费的数据库,小型的数据库,已经被Oracle收购了,MySQL6.x版本也开始收费
      2. Oracle : 收费的大型数据库,Oracle公司的产品,Oracle收购SUN公司,收购MySQL.
      3. DB2 : IBM公司的数据库产品,收费的,常用在银行系统中
      4. SQLServer : Microsoft 公司收费的中型的数据库,C#,.net等语言常使用
      5. SyBase : 已经淡出历史舞台,提供了一个非常专业的建模工具PowerDesigner
      6. SQLLite: 嵌入式的小型数据库,应用在手机端.

    MySQL数据库软件

    1. 安装
      1. 自行在网络上搜索安装教程
    2. 卸载
      1. 去MySQL的安装目录下找到my.ini文件 -->找到 datadir=“C:/ProgramData/MySQL/…”
      2. 卸载MySQL
      3. 删除C:/ProgramData目录下的MySQL文件夹
        3.配置
      4. MySQL服务启动
        1. 手动
        2. 使用管理员打开cmd
          1. net start mysql : 启动mysql服务
          2. net stop mysql : 关闭mysql服务
            2.MySQL登录
        3. mysql -uroot -p密码
        4. mysql -hip -uroot -p连接目标的密码
          3.MySQL的退出
        5. exit
        6. quit
          4.MySQL目录结构
        7. MySQL安装目录
          • 配置文件 : my.ini
        8. MySQL数据目录
          • 数据库 : 文件夹
          • 表 : 文件
          • 数据 : 文件中的内容

    SQL

    1. 什么是SQL?
      概念: Structured Query Language : 结构化查询语言
      其实就是定义了操作所有关系型数据库的规则,每一种数据库操作的方式存在不一样的地方,称为方言.
    2. SQL通用语法
      1. SQL语句可以以单行或者多行书写,以分号结尾.
      2. 可使用空格和缩进来增强语句的可读性.
      3. MySQL 数据库的SQL语句不区分大小写,关键字建议使用大写.
      4. 三种注释
        1. 单行注释 : – 注释内容 或 # 注释内容
        2. 多行注释 : /* 注释内容 */
    3. SQL分类:
      1. DDL(操作数据库,表) : create , drop , alter 等
      2. DML(增删改表中的数据):insert ,delete ,update 等
      3. DQL(查询表中的数据) : select ,where
      4. DCL(授权) : grant , revoke 等

    DDL : 操作数据库,表

    1. 操作数据库 : CRUD
      1. C(Create) : 创建
        1. 创建db1数据库,判断是否存在,并指定字符集为gbk
          • create db1 if not exists character set gbk;
      2. R(Retrieve) : 查询
        1. 查询素有数据库的名称
          • show databases;
        2. 查询某个数据库的字符集 : 查询某个数据库的创建语句
          • show create database 数据库名称.
      3. U(Update) : 修改
        1. 修改数据库的字符集 : alter 数据库 character set utf8;
      4. D(Delete) : 删除
        1.判断数据库,存在再进行删除 : drop database if exists 数据库名称
      5. 使用数据库
        1. use database 数据库名称.
    2. 操作表 : CRUD
      1. C(Create) : 创建
        1. 语法 :
          create table 表名(
          列名1 数据类型1,
          列名2 数据类型2,

          列名n 数据类型n
          )
          create table tab1 like tab2;
        2. 数据类型
          1. int : 整数类型
          2. double : 小数类型
          3. date : 日期,只包含年月日, yyyy-MM-dd
          4. datetime : 日期,包含年月日时分秒 yyyy-MM-dd HH-mm-ss
          5. timestamp: 包含年月日时分秒 yyyy-MM-dd HH-mm-ss, 如果将来不给这个字段赋值,或者赋值为null,则默认使用当前的系统时间来自动填充.
          6. varchar : 字符串类型(20)
        3. 创建表
          create table student(
          name varchar(20),
          age int,
          gender varchar(10),
          birthday timestamp,
          score double(4,1)
          );
      2. R(Retrieve) : 查询
        • 查询某个数据库中所有的表名称
          • show tables
        • 查询表结构
          • desc 表名
      3. U(Update) : 修改
        1. 修改表名
          alter table 表名 rename to 新的表名
        2. 修改表的字符集
          alter table 表名 character set 字符集名称;
        3. 添加一列
          alter table 表名 add 列名 数据类型
        4. 修改列名称 类型
          alter table 表名 change 列名 新列名 新数据类型
          alter table 表名 modify 列名 新数据类型
        5. 删除列
          alter table 表名 drop 列名
      4. D(Delete) : 删除
        1. drop table 表名
        2. drop table if exists 表名;

    DML 增删改表中

    1. 添加数据:
      * 语法 : insert into 表名(列名1,列名2,…列名n) values(值1,值2,…值n);
      * 注意 :
      1. 列名和值要–对应.
      2. 如果表名后,不定义列名,则默认给所有列添加值
      insert into 表名 values(值1,值2,…值n)
      3. 除了数字类型,其它类型需要使用引号(单双都可以引起来).
    2. 删除数据
      • 语法 : delete from 表名 []
      • 注意:
        1. 如果不加条件,则删除表中所有记录
        2. 如果要删除所有记录
          1. delete from 表名; – 不推荐使用,有多少条记录就会执行多少次删除操作.
          2. truncate table 表名 – 推荐使用,效率更高,先删除表,然后再创建一张一样的表.
    3. 修改数据
      • update 表名 set 列名1 = 数据 1 ,列名2 = 数据 2 ,…[where 条件]]
      • 注意 : 如果不加条件,则会把所有表中所有数据进行修改.

    DQL : 查询表中的记录

    1. 语法 :
    	 select 
    			字段列表
    	 from 
    	 		表名列表 
    	 where
    		    条件列表 
    	 group by
    	  		 分组字段 
    	 having
    	   		分组之后的条件 
    	 order by
    	  		 排序
    	 limit
    	  		 分页限定
    
    1. 基础查询

      1. 多个字段的查询
         select 
        		name,	-- 姓名
        		age		-- 年龄
         from 
         		student; -- 学生表
        
      2. 去除重复
        	select distinct address from student;
        
      3. 计算列
        	select name, math, english, math+ ifnull(english,0) from student;
        
      4. 起别名
        select name, math, english, math+ ifnull(english,0) 总分 from student;
    2. 条件查询

      1. where子语句后跟条件
      2. 运算符
          1. > , < , <= , >= , = <>
          2. between ... and
          3. in (集合)
          4. like
          	1. 占位符 :   _: 单个占位符   % :  多个任意占位符
          5. is null
          6. and 或 &&
          7. or 或 ||
          8. not 或 !
          9. 默认 : sex char(1) default '男';
        
    3. 排序查询

      1. 语法 : order by 子句
        * order by 排序字段1,排序字段2
      2. 排序方式 :
        * ASC : 升序,默认的
        * DESC : 降序
      3. 注意 : 如果有多个排序条件,则当前边的条件值一样时,才会判断第二条件.
    4. 聚合函数 : 将一列数据作为一个整体,进行纵向的计算.

      • count : 计算个数 – select count(id) from student.
      • max ; 计算最大值
      • min : 计算最小值
      • sum : 计算和 – select sum(math) from student.
      • avg : 计算平均值
        • 注意 : 聚合函数得计算,排除null值
        • 解决方案 :
          1. 选择不包含非空的列进行计算
          2. IFNULL函数
    5. 分组查询 :

      1. 语法 : group by 分组字段 ;
        1. 示例 : select sex avg(math) ,count(id) personNum from student where math > 75 having personNum > 2 ;
      2. 注意 :
        1. 分组之后查询的字段 : 分组字段,聚合函数
        2. where 和 having 的区别?
          1. where 在分组之前进行限定,如果不满足条件,则不参与分组,having在分组之后进行限定,如果不满足结果,则不会被查询出来.
          2. where 后边不能跟聚合函数,having可以进行聚合函数的判断.
    6. 分页查询

      1. 语法 : limit 开始的索引,每页查询的条数
      2. 公式 : 开始的索引 = (当前的页码数 - 1 ) * 每页显示的条数

    约束

    1. 概念 : 对表中的数据进行限定,保证数据的正确性,有效性和完整性.
    2. 分类 :
      1. 主键约束 : primary key
        1. 注意
          1. 含义 : 非空且唯一
          2. 一张表只有一个字段为主键
          3. 主键就是表中记录的唯一标识
        2. 创建 : id varchar(10) primary key.
        3. 删除主键 : alter table drop primary key
        4. 自动增长 : auto_increment
        5. 联合主键 : primary key (uid,rid);
      2. 非空约束 : not null
        • 创建时添加约束 : name varchar(20) not null, – name列的值不能为空
        • 创建后添加约束 : alter table stu modify name varchar(20) not null, – name列的值不能为空
        • 删除约束 : alter table stu modify name varchar(20) – 修改name列的值可以为空
      3. 唯一约束 : unique
        • 添加约束: phone varchar(20) unique – 表示手机号不能重复
        • 注意 : null值在手机号码列是可以重复的.
        • 删除约束 : alter table stu drop index phone;
      4. 外键约束 : foreign key
        1. 在创建表时,可以添加外键
          • 语法
            create table 表名(

            外键列
            constraint 外键名称 foreign key (外键列名称) reference 主表名称(主表列名称)
            );
        2. 删除外键 : alter table stu foreign key 外键名称
        3. 添加外键 alter table stu add constraint stu_clas_fk foreign key (clas_id) reference class(id);
        4. 级联操作 :
          • 语法 : 添加外键时 添加级联更新和级联删除
          • alter table stu add constraint stu_clas_fk foreign key (clas_id) reference class(id) on update casecade on delete casecade;

    数据库的设计

    1. 多表之间的关系
      1. 分类
        1. 一对一 :
          • 如 : 人和身份证
          • 分析 : 一个人只有一个身份证,一个身份证只能对应一个人
        2. 一对多(多对一) :
          • 如 : 部门和员工
          • 分析 : 一个部们有多个员工,一个员工只能对应一个部门
        3. 多对多
          • 如 : 学生和课程
          • 分析 : 一个学生可以选择很多门课程,一个课程也可以被很多学生选择.
      2. 实现关系 :
        1. 一对多(多对一) :
          • 如 : 部门和员工
          • 实现方式 : 在多的一方建立外键,指向一的一方的主键.
        2. 多对多
          • 如 : 学生和课程
          • 实现方式 : 多对多关系实现需要借助第三张中间表,中间表至少包含两个字段,这两个字段作为第三张表的外键,分别指向两张表的主键.
        3. 一对一
          • 如 : 人和身份证
          • 实现方式 : 一对一关系实现,可以在任意一方添加唯一外键指向另一方的主键.
    2. 数据库设计的范式
      • 概念 : 设计关系数据库时,遵从不同的规范要求,设计出合理的关系型数据库,这些不同的规范要求被称为不同的范式,各种范式呈递次规范,越高的范式数据库冗余越小。
      • 分类 :
        1. 第一范式(1NF).: 每一列都是不可分割的原子数据项
        2. 第二范式(2NF) : 在1NF的基础上,非码属性必须完全依赖于候选码(在1NF基础上消除非主属性对主码的部分函数依赖)
          • 几个概念 :
            • 函数依赖 : A – > B , 如果通过A属性(属性组)的值,可以确定唯一B属性的值,则称B依赖于A.
              例如 : 学号 --> 姓名 (学号,课程名称) --> 分数
            • 完全函数依赖 : A–>B , 如果A是一个属性组,则B属性值确定需要A属性组中的所有属性值.
              例如:(学号,课程名称) --> 分数
            • 部分函数依赖 : 如果A是一个属性组,则B属性值确定需要A属性组中的某一个属性值.
            • 传递函数依赖 : A–>B , B–>C, A属性的值确定B属性的值,B属性的值能确定C属性的值.
              例如 : 学号 --> 系名 --> 系主任
            • 码 : 如果在一张表中,一个属性或属性组,被其它所有属性所完全依赖,则称这个属性(属性组)为该表的码.
              例如 : 表中的码为 : (学号,课程名称)
              • 主属性 : 码属性组中的所有属性
              • 非主属性 : 除过码属性组的属性
        3. 第三范式(3NF) : 在2NF基础上,任何非主属性不依赖于其它非主属性(在2NF基础上消除传递依赖)

    数据库的备份和还原

    1. 命令行 :
      • 语法 : mysqldump -u用户名 -p密码 数据库名 > 保存的路径
      • source 保存的路径.

    多表查询

    1. 查询语法 :
      select
      	列名列表
      from
      	表名列表
      where ...
      
    2. 笛卡尔积 ;
      • 有两个集合A,B. 取这两个集合的所有组成情况
      • 要完成多表查询,需要消除无用的数据
    3. 多表查询的分类 :
      1. 内连接查询 :
        1. 隐式内连接 : 使用where条件消除无用的数据
          如 : 查询员工表的名称,性别,部门表的名称 
             select 
          		t1.name,		-- 员工表的姓名
          		t1.gender,		-- 员工表的性别
          		t2.name		-- 部门表的名称
          	from
          		emp t1,
          		dept t2
          	where 
          		t1.dept_id = t2.id;
          
        2. 显式内连接
          1. 语法 : select 字段列表 from 表名1 [ inner ] join 表名2 on 条件.
        3. 内连接查询注意事项:
          1. 从哪些表中查询数据
          2. 条件是什么
          3. 查询哪些字段
      2. 外连接查询 :
        1. 左外连接 :
        1. 语法 : select 字段列表 from 表1 left [ outer ] join 表2 on 条件 ;
        2. 查询的是左表所有数据以及其交集部分
        2. 右外连接 :
        1. 语法 : select 字段列表 from 表1 right [ outer ] join 表2 on 条件 ;
        2. 查询的是右表所有数据以及其交集部分
      3. 子查询 :
        • 概念 : 查询中嵌套查询,称嵌套查询为子查询
        • 如 : 查询工资最高的员工信息
          select * from emp where salary = (select max(salary) from emp);
        • 子查询不同情况
          1. 子查询的结果是单行,单列的
            • 子查询可以作为条件,使用运算符去判断. 运算符 : > < =;
          2. 子查询的结果是多行单列的 :
            • 子查询可以作为条件,使用运算符in来判断
          3. 子查询的结果是多行多列的 :
            • 子查询可以作为一张虚拟表参与查询
      4. 编写SQL思路
        1. 首先确定查询的内容在哪个表
        2. 查询的条件
        3. 需要使用哪种查询方式
        4. 编写sql

    事务

    1. 事务的基本介绍
      1. 概念 :
        • 如果一个包含多个步骤的业务操作,被事务管理,那么这些操作要么同时成功,要么同时失败
      2. 操作 :
        1. 开启事务 : start transaction;
        2. 回滚 : rollback;
        3. 提交 : commit;
      3. MySQL数据库中事务默认自动提交
        1. 自动提交 :
          • mysql就是自动提交的
          • 一条DML(增删改)语句会自动提交一次事务
        2. 手动提交
          • Oracle 数据库默认是手动提交事务
          • 需要先开启事务,再提交
        3. 修改事务的默认提交方式 :
          • 查看事务的默认提交方式 : select @@autocommit ; – 1 代表自动提交. – 0 代表手动提交
          • 修改默认提交方式 : set @@autocommit = 0 ;
    2. 事务的四大特征 :
      1. 原子性 : 是不可分割的最小操作单位,要么同时成功,要么同时失败.
      2. 持久性 : 当事务提交或回滚后,数据库会持久化保存数据
      3. 隔离性 : 多个事务之间,相互独立
      4. 一致性 : 事务操作前后,数据总量不变
    3. 事务的隔离级别 :
      • 概念 : 多个事务之间隔离的,相互独立的,但是如果多个事务操作同一批数据,则会引发一些问题.
      • 存在问题 :
        1. 脏读 : 一个事务,读取到另一个 事务中没有提交的数据
        2. 不可重复读(虚读) : 在同一个事务中,两次读取到的数据不一样
        3. 幻读 : 一个事务操作(DML)数据表中的所有记录,另一个事务添加了一条数据,则第一个事务查询不到自己的修改
      • 隔离级别 :
        1. read uncommitted : 读未提交
          • 产生的问题 : 脏读,不可重复读,幻读
        2. read commited : 读已提交 (Oracle默认)
          • 产生的问题 : 不可重复读,幻读
        3. repeatable read : 可重复读 (MySQL默认)
          • 产生的问题 : 幻读
        4. serializable : 串行化
          • 可以解决所有的问题
        • 注意 : 隔离级别从小到大安全性越来越高,但是效率越来越低
        • 数据库查询隔离级别 :
          • select @@tx isolation
        • 数据库设置隔离级别 :
          • set global transaction isolation level 级别字符串.

    DCL

    1. DCL : 管理用户,授权
      1. 管理用户
        1. 添加用户
          • 语法 : create user ‘用户名’ @‘主机名’ identified by ‘密码’;
        2. 修改用户
          • 语法 : update user set password = passwword(‘新密码’) where user = ‘用户名’;
        3. 删除用户
          • 语法 : drop user ‘用户名’ @‘主机名’;
        4. 查询用户
      2. 权限
        1. 查询权限
          • 语法 : show grants for ‘用户名’@‘主机名’;
        2. 授予权限
          • 语法 : grant 权限列表 on 数据库名.表名 to ‘用户名’@‘主机名’;
          • 授予全部权限,全部表 : grant all on . to ‘用户名’@‘主机名’;
        3. 撤销权限
          • 语法 : revoke 权限列表 on 数据库名.表名 from ‘用户名’@‘主机名’;
    展开全文
  • 手把手教你利用爬虫爬网页(Python代码)

    万次阅读 多人点赞 2019-05-14 14:34:48
    万维网数据形式的丰富和网络技术的不断发展,图片、数据库、音频、视频多媒体等不同数据大量出现,通用搜索引擎往往对这些信息含量密集且具有一定结构的数据无能为力,不能很好地发现和获取。 通用搜索引擎大多提供...

    640?wx_fmt=jpeg

    本文主要分为两个部分:一部分是网络爬虫的概述,帮助大家详细了解网络爬虫;另一部分是HTTP请求的Python实现,帮助大家了解Python中实现HTTP请求的各种方式,以便具备编写HTTP网络程序的能力。


    01

    网络爬虫概述


    接下来从网络爬虫的概念、用处与价值和结构等三个方面,让大家对网络爬虫有一个基本的了解。

    1. 网络爬虫及其应用

    随着网络的迅速发展,万维网成为大量信息的载体,如何有效地提取并利用这些信息成为一个巨大的挑战,网络爬虫应运而生。网络爬虫(又被称为网页蜘蛛、网络机器人),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。下面通过图3-1展示一下网络爬虫在互联网中起到的作用:


    640?wx_fmt=png

    ▲图3-1 网络爬虫


    网络爬虫按照系统结构和实现技术,大致可以分为以下几种类型:通用网络爬虫、聚焦网络爬虫、增量式网络爬虫、深层网络爬虫。实际的网络爬虫系统通常是几种爬虫技术相结合实现的。

    搜索引擎(Search Engine),例如传统的通用搜索引擎baidu、Yahoo和Google等,是一种大型复杂的网络爬虫,属于通用性网络爬虫的范畴。但是通用性搜索引擎存在着一定的局限性:

    1. 不同领域、不同背景的用户往往具有不同的检索目的和需求,通用搜索引擎所返回的结果包含大量用户不关心的网页。

    2. 通用搜索引擎的目标是尽可能大的网络覆盖率,有限的搜索引擎服务器资源与无限的网络数据资源之间的矛盾将进一步加深。

    3. 万维网数据形式的丰富和网络技术的不断发展,图片、数据库、音频、视频多媒体等不同数据大量出现,通用搜索引擎往往对这些信息含量密集且具有一定结构的数据无能为力,不能很好地发现和获取。

    4. 通用搜索引擎大多提供基于关键字的检索,难以支持根据语义信息提出的查询。

    为了解决上述问题,定向抓取相关网页资源的聚焦爬虫应运而生。

    聚焦爬虫是一个自动下载网页的程序,它根据既定的抓取目标,有选择地访问万维网上的网页与相关的链接,获取所需要的信息。与通用爬虫不同,聚焦爬虫并不追求大的覆盖,而将目标定为抓取与某一特定主题内容相关的网页,为面向主题的用户查询准备数据资源。

    说完了聚焦爬虫,接下来再说一下增量式网络爬虫。增量式网络爬虫是指对已下载网页采取增量式更新和只爬行新产生的或者已经发生变化网页的爬虫,它能够在一定程度上保证所爬行的页面是尽可能新的页面。

    和周期性爬行和刷新页面的网络爬虫相比,增量式爬虫只会在需要的时候爬行新产生或发生更新的页面,并不重新下载没有发生变化的页面,可有效减少数据下载量,及时更新已爬行的网页,减小时间和空间上的耗费,但是增加了爬行算法的复杂度和实现难度。

    例如:想获取赶集网的招聘信息,以前爬取过的数据没有必要重复爬取,只需要获取更新的招聘数据,这时候就要用到增量式爬虫。

    最后说一下深层网络爬虫。Web页面按存在方式可以分为表层网页和深层网页。表层网页是指传统搜索引擎可以索引的页面,以超链接可以到达的静态网页为主构成的Web页面。深层网络是那些大部分内容不能通过静态链接获取的、隐藏在搜索表单后的,只有用户提交一些关键词才能获得的Web页面。

    例如用户登录或者注册才能访问的页面。可以想象这样一个场景:爬取贴吧或者论坛中的数据,必须在用户登录后,有权限的情况下才能获取完整的数据。

    2. 网络爬虫结构

    下面用一个通用的网络爬虫结构来说明网络爬虫的基本工作流程,如图3-4所示。


    640?wx_fmt=png

    ▲图3-4 网络爬虫结构

    网络爬虫的基本工作流程如下:

    1. 首先选取一部分精心挑选的种子URL。

    2. 将这些URL放入待抓取URL队列。

    3. 从待抓取URL队列中读取待抓取队列的URL,解析DNS,并且得到主机的IP,并将URL对应的网页下载下来,存储进已下载网页库中。此外,将这些URL放进已抓取URL队列。

    4. 分析已抓取URL队列中的URL,从已下载的网页数据中分析出其他URL,并和已抓取的URL进行比较去重,最后将去重过的URL放入待抓取URL队列,从而进入下一个循环。

    02

    HTTP请求的Python实现


    通过上面的网络爬虫结构,我们可以看到读取URL、下载网页是每一个爬虫必备而且关键的功能,这就需要和HTTP请求打交道。接下来讲解Python中实现HTTP请求的三种方式:urllib2/urllib、httplib/urllib以及Requests。

    1. urllib2/urllib实现

    urllib2和urllib是Python中的两个内置模块,要实现HTTP功能,实现方式是以urllib2为主,urllib为辅。

    1.1 首先实现一个完整的请求与响应模型

    urllib2提供一个基础函数urlopen,通过向指定的URL发出请求来获取数据。最简单的形式是:

    
     

    import urllib2
    response=urllib2.urlopen('http://www.zhihu.com')
    html=response.read()
    print html

    其实可以将上面对http://www.zhihu.com的请求响应分为两步,一步是请求,一步是响应,形式如下:

    
     

    import urllib2
    # 请求
    request=urllib2.Request('http://www.zhihu.com')
    # 响应
    response = urllib2.urlopen(request)
    html=response.read()
    print html

    上面这两种形式都是GET请求,接下来演示一下POST请求,其实大同小异,只是增加了请求数据,这时候用到了urllib。示例如下:

    
     

    import urllib
    import urllib2
    url = 'http://www.xxxxxx.com/login'
    postdata = {'username' : 'qiye',
        'password' : 'qiye_pass'}
    # info 需要被编码为urllib2能理解的格式,这里用到的是urllib
    data = urllib.urlencode(postdata)
    req = urllib2.Request(url, data)
    response = urllib2.urlopen(req)
    html = response.read()

    但是有时会出现这种情况:即使POST请求的数据是对的,但是服务器拒绝你的访问。这是为什么呢?问题出在请求中的头信息,服务器会检验请求头,来判断是否是来自浏览器的访问,这也是反爬虫的常用手段。

    1.2 请求头headers处理

    将上面的例子改写一下,加上请求头信息,设置一下请求头中的User-Agent域和Referer域信息。

    
     

    import urllib
    import urllib2
    url = 'http://www.xxxxxx.com/login'
    user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
    referer='http://www.xxxxxx.com/'
    postdata = {'username' : 'qiye',
        'password' : 'qiye_pass'}
    # 将user_agent,referer写入头信息
    headers={'User-Agent':user_agent,'Referer':referer}
    data = urllib.urlencode(postdata)
    req = urllib2.Request(url, data,headers)
    response = urllib2.urlopen(req)
    html = response.read()

    也可以这样写,使用add_header来添加请求头信息,修改如下:

    
     

    import urllib
    import urllib2
    url = 'http://www.xxxxxx.com/login'
    user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
    referer='http://www.xxxxxx.com/'
    postdata = {'username' : 'qiye',
        'password' : 'qiye_pass'}
    data = urllib.urlencode(postdata)
    req = urllib2.Request(url)
    # 将user_agent,referer写入头信息
    req.add_header('User-Agent',user_agent)
    req.add_header('Referer',referer)
    req.add_data(data)
    response = urllib2.urlopen(req)
    html = response.read()

    对有些header要特别留意,服务器会针对这些header做检查,例如:

    • User-Agent:有些服务器或Proxy会通过该值来判断是否是浏览器发出的请求。

    • Content-Type:在使用REST接口时,服务器会检查该值,用来确定HTTP Body中的内容该怎样解析。在使用服务器提供的RESTful或SOAP服务时,Content-Type设置错误会导致服务器拒绝服务。常见的取值有:application/xml(在XML RPC,如RESTful/SOAP调用时使用)、application/json(在JSON RPC调用时使用)、application/x-www-form-urlencoded(浏览器提交Web表单时使用)。

    • Referer:服务器有时候会检查防盗链。

    1.3 Cookie处理

    urllib2对Cookie的处理也是自动的,使用CookieJar函数进行Cookie的管理。如果需要得到某个Cookie项的值,可以这么做:

    
     

    import urllib2
    import cookielib
    cookie = cookielib.CookieJar()
    opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie))
    response = opener.open('http://www.zhihu.com')
    for item in cookie:
        print item.name+':'+item.value

    但是有时候会遇到这种情况,我们不想让urllib2自动处理,我们想自己添加Cookie的内容,可以通过设置请求头中的Cookie域来做:

    
     

    import  urllib2
    opener = urllib2.build_opener()
    opener.addheaders.append( ( 'Cookie''email=' + "xxxxxxx@163.com" ) )
    req = urllib2.Request( "http://www.zhihu.com/" )
    response = opener.open(req)
    print response.headers
    retdata = response.read()

    1.4 Timeout设置超时

    在Python2.6之前的版本,urllib2的API并没有暴露Timeout的设置,要设置Timeout值,只能更改Socket的全局Timeout值。示例如下:

    
     

    import urllib2
    import socket
    socket.setdefaulttimeout(10# 10 秒钟后超时
    urllib2.socket.setdefaulttimeout(10# 另一种方式

    在Python2.6及新的版本中,urlopen函数提供了对Timeout的设置,示例如下:

    
     

    import urllib2
    request=urllib2.Request('http://www.zhihu.com')
    response = urllib2.urlopen(request,timeout=2)
    html=response.read()
    print html

    1.5 获取HTTP响应码

    对于200 OK来说,只要使用urlopen返回的response对象的getcode()方法就可以得到HTTP的返回码。但对其他返回码来说,urlopen会抛出异常。这时候,就要检查异常对象的code属性了,示例如下:

    
     

    import urllib2
    try:
        response = urllib2.urlopen('http://www.google.com')
        print response
    except urllib2.HTTPError as e:
        if hasattr(e, 'code'):
            print 'Error code:',e.code

    1.6 重定向

    urllib2默认情况下会针对HTTP 3XX返回码自动进行重定向动作。要检测是否发生了重定向动作,只要检查一下Response的URL和Request的URL是否一致就可以了,示例如下:

    
     

    import urllib2
    response = urllib2.urlopen('http://www.zhihu.cn')
    isRedirected = response.geturl() == 'http://www.zhihu.cn'

    如果不想自动重定向,可以自定义HTTPRedirectHandler类,示例如下:

    
     

    import urllib2
    class RedirectHandler(urllib2.HTTPRedirectHandler):
        def http_error_301(self, req, fp, code, msg, headers):
            pass
        def http_error_302(self, req, fp, code, msg, headers):
            result = urllib2.HTTPRedirectHandler.http_error_301(self, req, fp, code, 
            msg, headers)
            result.status = code
            result.newurl = result.geturl()
            return result
    opener = urllib2.build_opener(RedirectHandler)
    opener.open('http://www.zhihu.cn')

    1.7 Proxy的设置

    在做爬虫开发中,必不可少地会用到代理。urllib2默认会使用环境变量http_proxy来设置HTTP Proxy。但是我们一般不采用这种方式,而是使用ProxyHandler在程序中动态设置代理,示例代码如下:

    
     

    import urllib2
    proxy = urllib2.ProxyHandler({'http''127.0.0.1:8087'})
    opener = urllib2.build_opener([proxy,])
    urllib2.install_opener(opener)
    response = urllib2.urlopen('http://www.zhihu.com/')
    print response.read()

    这里要注意的一个细节,使用urllib2.install_opener()会设置urllib2的全局opener,之后所有的HTTP访问都会使用这个代理。这样使用会很方便,但不能做更细粒度的控制,比如想在程序中使用两个不同的Proxy设置,这种场景在爬虫中很常见。比较好的做法是不使用install_opener去更改全局的设置,而只是直接调用opener的open方法代替全局的urlopen方法,修改如下:

    
     

    import urllib2
    proxy = urllib2.ProxyHandler({'http''127.0.0.1:8087'})
    opener = urllib2.build_opener(proxy,)
    response = opener.open("http://www.zhihu.com/")
    print response.read()


    2. httplib/urllib实现

    httplib模块是一个底层基础模块,可以看到建立HTTP请求的每一步,但是实现的功能比较少,正常情况下比较少用到。在Python爬虫开发中基本上用不到,所以在此只是进行一下知识普及。下面介绍一下常用的对象和函数:


    • 创建HTTPConnection对象:

      class httplib.HTTPConnection(host[, port[, strict[, timeout[, source_address]]]])。

    • 发送请求:

      HTTPConnection.request(method, url[, body[, headers]])。

    • 获得响应:

      HTTPConnection.getresponse()。

    • 读取响应信息:

      HTTPResponse.read([amt])。

    • 获得指定头信息:

      HTTPResponse.getheader(name[, default])。

    • 获得响应头(header, value)元组的列表:

      HTTPResponse.getheaders()。

    • 获得底层socket文件描述符:

      HTTPResponse.fileno()。

    • 获得头内容:

      HTTPResponse.msg。

    • 获得头http版本:

      HTTPResponse.version。

    • 获得返回状态码:

      HTTPResponse.status。

    • 获得返回说明:

      HTTPResponse.reason。

    接下来演示一下GET请求和POST请求的发送,首先是GET请求的示例,如下所示:

    
     

    import httplib
    conn =None
    try:
        conn = httplib.HTTPConnection("www.zhihu.com")
        conn.request("GET""/")
        response = conn.getresponse()
        print response.status, response.reason
        print '-' * 40
        headers = response.getheaders()
        for h in headers:
            print h
        print '-' * 40
        print response.msg
    except Exception,e:
        print e
    finally:
        if conn:
            conn.close()

    POST请求的示例如下:

    
     

    import httplib, urllib
    conn = None
    try:
        params = urllib.urlencode({'name''qiye''age'22})
        headers = {"Content-type""application/x-www-form-urlencoded"
        , "Accept""text/plain"}
        conn = httplib.HTTPConnection("www.zhihu.com"80, timeout=3)
        conn.request("POST""/login", params, headers)
        response = conn.getresponse()
        print response.getheaders() # 获取头信息
        print response.status
        print response.read()
    except Exception, e:
        print e
        finally:
        if conn:
            conn.close()

    3. 更人性化的Requests

    Python中Requests实现HTTP请求的方式,是本人极力推荐的,也是在Python爬虫开发中最为常用的方式。Requests实现HTTP请求非常简单,操作更加人性化。

    Requests库是第三方模块,需要额外进行安装。Requests是一个开源库,源码位于:

    GitHub: https://github.com/kennethreitz/requests

    希望大家多多支持作者。

    使用Requests库需要先进行安装,一般有两种安装方式:

    • 使用pip进行安装,安装命令为:pip install requests,不过可能不是最新版。

    • 直接到GitHub上下载Requests的源代码,下载链接为:

      https://github.com/kennethreitz/requests/releases

      将源代码压缩包进行解压,然后进入解压后的文件夹,运行setup.py文件即可。

    如何验证Requests模块安装是否成功呢?在Python的shell中输入import requests,如果不报错,则是安装成功。如图3-5所示。

    640?wx_fmt=png

    ▲图3-5 验证Requests安装

    3.1 首先还是实现一个完整的请求与响应模型

    以GET请求为例,最简单的形式如下:

    
     

    import requests
    r = requests.get('http://www.baidu.com')
    print r.content

    大家可以看到比urllib2实现方式的代码量少。接下来演示一下POST请求,同样是非常简短,更加具有Python风格。示例如下:

    
     

    import requests
    postdata={'key':'value'}
    r = requests.post('http://www.xxxxxx.com/login',data=postdata)
    print r.content

    HTTP中的其他请求方式也可以用Requests来实现,示例如下:

    
     

    r = requests.put('http://www.xxxxxx.com/put', data = {'key':'value'})
    r = requests.delete('http://www.xxxxxx.com/delete')
    r = requests.head('http://www.xxxxxx.com/get')
    r = requests.options('http://www.xxxxxx.com/get')

    接着讲解一下稍微复杂的方式,大家肯定见过类似这样的URL:

    http://zzk.cnblogs.com/s/blogpost?Keywords=blog:qiyeboy&pageindex=1

    就是在网址后面紧跟着“?”,“?”后面还有参数。那么这样的GET请求该如何发送呢?肯定有人会说,直接将完整的URL带入即可,不过Requests还提供了其他方式,示例如下:

    
     

    import requests
        payload = {'Keywords''blog:qiyeboy','pageindex':1}
    r = requests.get('http://zzk.cnblogs.com/s/blogpost', params=payload)
    print r.url

    通过打印结果,我们看到最终的URL变成了:

    http://zzk.cnblogs.com/s/blogpost?Keywords=blog:qiyeboy&pageindex=1

    3.2 响应与编码

    还是从代码入手,示例如下:

    
     

    import requests
    r = requests.get('http://www.baidu.com')
    print 'content-->'+r.content
    print 'text-->'+r.text
    print 'encoding-->'+r.encoding
    r.encoding='utf-8'
    print 'new text-->'+r.text

    其中r.content返回的是字节形式,r.text返回的是文本形式,r.encoding返回的是根据HTTP头猜测的网页编码格式。

    输出结果中:“text-->”之后的内容在控制台看到的是乱码,“encoding-->”之后的内容是ISO-8859-1(实际上的编码格式是UTF-8),由于Requests猜测编码错误,导致解析文本出现了乱码。Requests提供了解决方案,可以自行设置编码格式,r.encoding='utf-8'设置成UTF-8之后,“new text-->”的内容就不会出现乱码。

    但是这种手动的方式略显笨拙,下面提供一种更加简便的方式:chardet,这是一个非常优秀的字符串/文件编码检测模块。安装方式如下:

    
     

    pip install chardet

    安装完成后,使用chardet.detect()返回字典,其中confidence是检测精确度,encoding是编码形式。示例如下:

    
     

    import requests
    r = requests.get('http://www.baidu.com')
    print chardet.detect(r.content)
    r.encoding = chardet.detect(r.content)['encoding']
    print r.text

    直接将chardet探测到的编码,赋给r.encoding实现解码,r.text输出就不会有乱码了。

    除了上面那种直接获取全部响应的方式,还有一种流模式,示例如下:

    
     

    import requests
    r = requests.get('http://www.baidu.com',stream=True)
    print r.raw.read(10)

    设置stream=True标志位,使响应以字节流方式进行读取,r.raw.read函数指定读取的字节数。

    3.3 请求头headers处理

    Requests对headers的处理和urllib2非常相似,在Requests的get函数中添加headers参数即可。示例如下:

    
     

    import requests
    user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
    headers={'User-Agent':user_agent}
    r = requests.get('http://www.baidu.com',headers=headers)
    print r.content

    3.4 响应码code和响应头headers处理

    获取响应码是使用Requests中的status_code字段,获取响应头使用Requests中的headers字段。示例如下:

    
     

    import requests
    r = requests.get('http://www.baidu.com')
    if r.status_code == requests.codes.ok:
        print r.status_code# 响应码
        print r.headers# 响应头
        print r.headers.get('content-type')# 推荐使用这种获取方式,获取其中的某个字段
        print r.headers['content-type']# 不推荐使用这种获取方式
    else:
        r.raise_for_status()

    上述程序中,r.headers包含所有的响应头信息,可以通过get函数获取其中的某一个字段,也可以通过字典引用的方式获取字典值,但是不推荐,因为如果字段中没有这个字段,第二种方式会抛出异常,第一种方式会返回None。

    r.raise_for_status()是用来主动地产生一个异常,当响应码是4XX或5XX时,raise_for_status()函数会抛出异常,而响应码为200时,raise_for_status()函数返回None。

    3.5 Cookie处理

    如果响应中包含Cookie的值,可以如下方式获取Cookie字段的值,示例如下:

    
     

    import requests
    user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
    headers={'User-Agent':user_agent}
    r = requests.get('http://www.baidu.com',headers=headers)
    # 遍历出所有的cookie字段的值
    for cookie in r.cookies.keys():
        print cookie+':'+r.cookies.get(cookie)

    如果想自定义Cookie值发送出去,可以使用以下方式,示例如下:

    
     

    import requests
    user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
    headers={'User-Agent':user_agent}
    cookies = dict(name='qiye',age='10')
    r = requests.get('http://www.baidu.com',headers=headers,cookies=cookies)
    print r.text

    还有一种更加高级,且能自动处理Cookie的方式,有时候我们不需要关心Cookie值是多少,只是希望每次访问的时候,程序自动把Cookie的值带上,像浏览器一样。Requests提供了一个session的概念,在连续访问网页,处理登录跳转时特别方便,不需要关注具体细节。使用方法示例如下:

    
     

    import Requests
    oginUrl = 'http://www.xxxxxxx.com/login'
    s = requests.Session()
    #首先访问登录界面,作为游客,服务器会先分配一个cookie
    r = s.get(loginUrl,allow_redirects=True)
    datas={'name':'qiye','passwd':'qiye'}
    #向登录链接发送post请求,验证成功,游客权限转为会员权限
    r = s.post(loginUrl, data=datas,allow_redirects= True)
    print r.text

    上面的这段程序,其实是正式做Python开发中遇到的问题,如果没有第一步访问登录的页面,而是直接向登录链接发送Post请求,系统会把你当做非法用户,因为访问登录界面时会分配一个Cookie,需要将这个Cookie在发送Post请求时带上,这种使用Session函数处理Cookie的方式之后会很常用。

    3.6 重定向与历史信息

    处理重定向只是需要设置一下allow_redirects字段即可,例如:

    r=requests.get('http://www.baidu.com',allow_redirects=True)

    将allow_redirects设置为True,则是允许重定向;设置为False,则是禁止重定向。如果是允许重定向,可以通过r.history字段查看历史信息,即访问成功之前的所有请求跳转信息。示例如下:

    
     

    import requests
    r = requests.get('http://github.com')
    print r.url
    print r.status_code
    print r.history

    打印结果如下:

    
     

    https://github.com/
    200
    (<Response [301]>,)

    上面的示例代码显示的效果是访问GitHub网址时,会将所有的HTTP请求全部重定向为HTTPS。

    3.7 超时设置

    超时选项是通过参数timeout来进行设置的,示例如下:

    
     

    requests.get('http://github.com', timeout=2)

    3.8 代理设置

    使用代理Proxy,你可以为任意请求方法通过设置proxies参数来配置单个请求:

    
     

    import requests
    proxies = {
        "http""http://0.10.1.10:3128",
        "https""http://10.10.1.10:1080",
    }
    requests.get("http://example.org", proxies=proxies)

    也可以通过环境变量HTTP_PROXY和HTTPS_PROXY?来配置代理,但是在爬虫开发中不常用。你的代理需要使用HTTP Basic Auth,可以使用http://user:password@host/语法:

    
     

    proxies = {
        "http""http://user:pass@10.10.1.10:3128/",
    }


    03

    小结


    本文主要讲解了网络爬虫的结构和应用,以及Python实现HTTP请求的几种方法。希望大家对本文中的网络爬虫工作流程和Requests实现HTTP请求的方式重点吸收消化。

    本文摘编自《Python爬虫开发与项目实战》,经出版方授权发布。

    关于作者:范传辉,资深网虫,Python开发者,参与开发了多项网络应用,在实际开发中积累了丰富的实战经验,并善于总结,贡献了多篇技术文章广受好评。研究兴趣是网络安全、爬虫技术、数据分析、驱动开发等技术。

    640?wx_fmt=jpeg

    Python爬虫开发与项目实战

    扫码购买

    640?wx_fmt=png


    本书特色:

    • 由浅入深,从Python和Web前端基础开始讲起,逐步加深难度,层层递进。

    • 内容详实,从静态网站到动态网站,从单机爬虫到分布式爬虫,既包含基础知识点,又讲解了关键问题和难点分析,方便读者完成进阶。

    • 实用性强,本书共有9个爬虫项目,以系统的实战项目为驱动,由浅及深地讲解爬虫开发中所需的知识和技能。

    • 难点详析,对js加密的分析、反爬虫措施的突破、去重方案的设计、分布式爬虫的开发进行了细致的讲解。

    扫码购买

    640?wx_fmt=png

    展开全文
  • 以下是通过简单的自测巩固以上3个数据库体验实验的知识点的答案以及偷拍的照片。 1. 答案是Redis,它不是关系型数据库。 2. 3. 答案是100TB,这个可以从你前面的实验中可以找到答案! 4. 答案应该是D...
  • 2021年前端面试题及答案

    万次阅读 多人点赞 2020-02-11 19:29:34
    因为面试官都是对简历所写的项目经验进行深挖或者对你所掌握的基本技能进行考察。 前端工程化 1 什么是"前端工程化"? 前端工程化是使用软件工程的技术和方法来进行前端的开发流程、技术、工具、经验等规范化、标准...
  • 我们看招聘信息的时候,经常会看到这一点,需要具备SSH框架的技能;而且在大部分教学课堂中,也会把SSH作为最核心的教学内容。 但是,我们在实际应用中发现,SpringMVC可以完全替代Struts,配合注解的方式,编程...
  • MySQL学习总结

    千次阅读 2019-05-21 14:39:29
    春节期间看了一本mysql书《MySQL数据库应用从入门到精通》 觉得这本书相对简单、基础、实用、全面,我们大多数人喜欢搞一些高深的东西,而忽视一些简单基础的东西,在工作当中我们犯错的地方往往是那些简单基础的...
  • 前端面试题

    万次阅读 多人点赞 2019-08-08 11:49:01
    声明对象,添加属性,输出属性 91 匹配输入的字符:第一个必须是字母或下划线开头,长度5-20 91 检测变量类型 92 如何在HTML中添加事件,几种方法? 92 BOM对象有哪些,列举window对象? 92 请问代码实现 ...
  • 用户不需要掌握操作数据库对象的知识和技能。 交付的数据库组件:生产数据库包含用户订制的材料规范和适用的试验标准的数据,以及使用这些数据的查询、窗体和应用程序模块;首次交付的生产数据库包含保存生产数据的...
  • 核心技能部分.… 207 91数据集( Dataset) ·,, 207 911数据集( DataSet)简介…207 912数据集的结构 08 91.3数据集的工作原理 914创建 Data set对象…… 211 9,2 DataAdapter对象1211 92.1 DataAdapter对象简介… 211...
  • 通过对各个用户的调查,该学校人力...(19)员工劳动技能(劳动技能编号、技能级别、技能名称、技能备注); (20)员工工作经历(工作经历编号、开始时间、结束时间、员工工作单位、工作内容、离职原因、员工编号);
  • 数据库

    2019-04-12 15:05:27
    1.对数据库物理存储方式的描述称为(B ) A.外模式 B.内模式 C.概念模式 D.逻辑模式 2.关系数据模型的三个组成部分中,不包括(D ) A.完整性规则 B.数据结构C.数据操作 D.并发控制 有关系:教学(学号、教工号、...
  • 如何设计数据库表?

    千次阅读 2020-04-16 15:04:10
    如何评价数据库的优劣 本章的经验之谈,我觉得比较适合没有接触设计数据库的朋友,并且不会很深入,高手可以忽略啦。 工作中,我们根据需求进行数据库的设计,其实就是对表的字段设计,表中该有哪些字段是比较合理...
  • 软件测试面试题汇总

    万次阅读 多人点赞 2018-09-27 12:31:09
    转载自: ... 软件测试面试题汇总 测试技术面试题 ...........................................................................................................
  • 数据库的演进之路 数据库的发展-业务视角 数据库的技术演进之路 一、传统集中式数据库架构 1.查询分析处理过程 2.核心技术 二、分布式数据库架构 1. 数据分布 2. 分布式事务 3. 数据复制 4. 读写分离 5. ...
  • 所谓图数据库,它应用图理论(Graph Theory)可以存储实体的相关属性以及它们之间的关系信息。最常见例子就是社会网络中人与人之间的关系。相比于关系型数据库(比如MySQL等),图数据库更能胜任这方面的任务。   ...
  • EpicSevenDB.com社区数据库,用于提供游戏信息,例如英雄,神器,装备,技能以及任何其他需要喂食数据库的信息。 贡献 第2版​​直接从源获取数据,因此不再需要手动json编写。 但是,如果发现错误或缺少某些数据/...
  • JAVA上百实例源码以及开源项目

    千次下载 热门讨论 2016-01-03 17:37:40
     Java局域网通信——飞鸽传书源代码,大家都知道VB版、VC版还有Delphi版的飞鸽传书软件,但是Java版的确实不多,因此这个Java文件传输实例不可错过,Java网络编程技能的提升很有帮助。 Java聊天程序,包括服务端和...
  • 这是我在导师的监督下完成的项目,并上传到该项目以证明我在所从事的技能方面的能力。 该项目的总体目标是: 为关系数据库设计适当的结构以存储给定公司的销售数据 使用MySQL设置所述关系数据库 查询数据库以提供...
  •  第4章介绍建立新表和设置表的字段属性的方法与技巧。  第5章介绍数据表的操作、设置数据表格式、创建子数据表和表之间关系等内容。  第6章介绍选择查询、交叉表查询、参数查询、操作查询和SQL查询等内容。  第7...
  • 游戏数据库设计经验

    万次阅读 多人点赞 2019-07-13 18:31:18
    一、游戏模板数据库设计特点 软件行业一般数据库设计原则,”保持数据的完整性一致性“,”避免数据冗余“,”范式设计“。但游戏领域的游戏模板表设计上还需要考虑这些特点 1.1、对游戏程序只读,游戏程序只需要...
  • 属性系统-展示我PHP,MySQL和Javascript技能的任务。 我们借用了一些symfony组件来创建一个轻量级的自定义框架,其资产由npm webpack生成。 指示 要求 PHP 7.4 MYSQL 5.7 安装 创建一个数据库并将详细信息添加到.env...
  • 数据库的常用术语

    千次阅读 2019-10-07 16:24:28
    对象类型的集合,是对静态属性的描述。数据操作(表里面的数据): 是对数据库中的各种对象性数据,允许执行的操作的集合,如增删改查等;数据操作是对系统动态热性的描述。数据约束条件(表里边的约束): 是一...
  • 数据库服务器的安装与配置

    万次阅读 多人点赞 2017-04-18 13:02:25
    数据库服务器是当今应用最为广泛的一种服务器类型,许多企业在信息化建设过程中都要购置数据库服务器。数据库服务器主要用于存储、查询、检索企业内部的信息,因此需要搭配专用的数据库系统,对服务器的兼容性、可靠...
  • 一句话总结:把人物的属性抽离出去,其它的装备啊,技能表和属性表之间建立一个关系表。   1、装备表如何设计(装备表和角色表的对应关系是什么)? 装备属于人物,所以装备表可以加上所属人物的id 装备,包括...
  • 王者荣耀所有英雄 hero_id 英雄id name 名称 pay_type 10周免英雄,11新手推荐 new_type 是否为最新英雄 hero_type 英雄类型 hero_type2 英雄类型(一个英雄可以属于两种类型)...skill 技能属性 difficulty 困难度属性
  • 脆弱性往往在系统上讲指安全性,不堪一击,这里的脆弱性指与数据库服务相关的所有质量属性问题:故障、服务失败、同步失效、数据恢复、同步不一致、数据有效性、成本可控性,凡是命中在数据库上所有的问题都属于脆弱...
  • 数据库设计

    千次阅读 多人点赞 2020-06-01 15:31:44
    数据库设计数据库设计概述需求分析概念结构设计逻辑结构设计物理结构设计数据库的实施和维护 本文最初由security9968发布于security9968的csdn博客,禁止任何形式的剽窃行为 转载原创文章请注明,转载自:security...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 25,833
精华内容 10,333
关键字:

数据库技能属性